GroupedRowIterator.java :  » Database-DBMS » axion » org » axiondb » engine » rowiterators » Java Open Source

Java Open Source » Database DBMS » axion 
axion » org » axiondb » engine » rowiterators » GroupedRowIterator.java
/*
 * $Id: GroupedRowIterator.java,v 1.23 2005/12/20 18:32:41 ahimanikya Exp $
 * =======================================================================
 * Copyright (c) 2003-2005 Axion Development Team.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The names "Tigris", "Axion", nor the names of its contributors may
 *    not be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. Products derived from this software may not be called "Axion", nor
 *    may "Tigris" or "Axion" appear in their names without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * =======================================================================
 */

package org.axiondb.engine.rowiterators;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.axiondb.AxionException;
import org.axiondb.Literal;
import org.axiondb.OrderNode;
import org.axiondb.Row;
import org.axiondb.RowComparator;
import org.axiondb.RowDecorator;
import org.axiondb.RowIterator;
import org.axiondb.Selectable;
import org.axiondb.engine.rows.SimpleRow;
import org.axiondb.engine.visitors.FindAggregateFunctionVisitor;
import org.axiondb.functions.AggregateFunction;
import org.axiondb.functions.ConcreteFunction;
import org.axiondb.util.ComparatorChain;

/**
 * Processes a "raw" iterator to implement GROUP BY functionality.
 * 
 * @version $Revision: 1.23 $ $Date: 2005/12/20 18:32:41 $
 * @author Rahul Dwivedi
 * @author Rod Waldhoff
 * @author Ahimanikya Satapathy
 * @author Girish Patil
 * @author Jonathan Giron
 */
public class GroupedRowIterator extends DelegatingRowIterator {

    public GroupedRowIterator(boolean sort, RowIterator rows, Map fieldMap, List groupBy, List selected, Selectable having, Selectable where,
            List orderBy) throws AxionException {
        super(null);

        _colIdToFieldMap = fieldMap;
        _groupByCols = groupBy;
        _selected = selected;
        _having = having;
        _where = where;
        _orderByNodes = orderBy;
        _aggrFnValueCache = new HashMap(_selected.size());

        SortedMap groupedRowMap = null;
        // If rows are not sorted then we need to sort them
        if (!isEmptyGroupBy() && sort) {
            groupedRowMap = doSort(rows);
        }

        // Determine in advance whether elements of Selectable list is an aggregate
        // function, so that we don't have to determines while making row for each group.
        // Keep the last element of the array reserved for having clause.
        _isAggregateFunction = new boolean[_selected.size() + 1];
        for (int i = 0, I = _selected.size();  i < I; i++) {
            _isAggregateFunction[i] = isAggregateFunction(_selected.get(i));
        }
        _isAggregateFunction[_selected.size()] = isAggregateFunction(_having);

        List grList = null;
        if (groupedRowMap == null) {
            grList = makeGroupRows(rows);
        } else {
            grList = makeGroupRows(groupedRowMap);
        }
        setDelegate(new ListRowIterator(grList));
    }

    public GroupedRowIterator(RowIterator rows, Map fieldMap, List groupBy, List selected, Selectable having, List orderBy) throws AxionException {
        this(true, rows, fieldMap, groupBy, selected, having, null, orderBy);
    }

    /** Not supported in the base implementation. */
    public void add(Row row) throws AxionException {
        throw new UnsupportedOperationException();
    }

    /** Not supported in the base implementation. */
    public void set(Row row) throws AxionException {
        throw new UnsupportedOperationException();
    }

    /** Not supported in the base implementation. */
    public void remove() throws AxionException {
        throw new UnsupportedOperationException();
    }

    public String toString() {
        StringBuffer buf = new StringBuffer(20);
        buf.append("Grouped(").append(_groupByCols == null ? "" : _groupByCols.toString());
        if(_having != null) {
            buf.append("; having=").append(_having).append(";");
        }
        buf.append("preSorted=").append(_preSorted).append(")");
        return  buf.toString();
    }

    // Returns true if the HAVING node evaluates to true
    private boolean acceptable(RowIterator currGroupRowIter, RowDecorator dec) throws AxionException {
        if (_having == null) {
            return true;
        }

        if (_isAggregateFunction[_selected.size()]) {
            Object val = evaluateAggregateFunction(dec, (ConcreteFunction) _having, currGroupRowIter);
            return ((Boolean) val).booleanValue();
        }
        
        // ISO/IEC 9075-2:2003, Section 7.10, General Rule 1 - clause is applied if having condition
        // evaluates to true; null evaluation thus maps to false.
        Boolean result = (Boolean) _having.evaluate(dec); 
        return (result == null) ? false : result.booleanValue();
    }

    private void addToCurrentGroup(RowDecorator dec, Row row, List currGroup) throws AxionException {
        if (_where == null) {
            currGroup.add(row);
            return;
        }

        // Else add to current group if WHERE node is null or evaluates to true
        dec.setRow(row);
        
        Boolean result = (Boolean) _where.evaluate(dec); 
        if (result != null && result.booleanValue()) {
            currGroup.add(row);
        }
    }

    // Collect rows for each group, groups will be maintained in natual sort order.
    private SortedMap doSort(RowIterator rows) throws AxionException {
        ComparatorChain sortChain = generateOrderChain();
        SortedMap groupedRows = new TreeMap(sortChain);
        RowDecorator dec = new RowDecorator(_colIdToFieldMap);
        while (rows.hasNext()) {
            Row row = rows.next();
            List currGroup = (List) groupedRows.get(row);
            if (currGroup == null) {
                currGroup = new ArrayList();
                groupedRows.put(row, currGroup);
            }
            addToCurrentGroup(dec, row, currGroup);
        }
        _preSorted = false;
        return groupedRows;
    }

    private Object evaluateAggregateFunction(RowDecorator dec, ConcreteFunction fn, RowIterator rows) throws AxionException {
        if (fn instanceof AggregateFunction) {
            rows.reset();
            AggregateFunction vfn = (AggregateFunction) fn;
            Object val = _aggrFnValueCache.get(vfn);
            if (val == null) {
                val = vfn.evaluate(new RowIteratorRowDecoratorIterator(rows, dec));
                _aggrFnValueCache.put(vfn, val);
            }
            return val;
        } else {
            // Aggregate function might have been nested with another aggregate or scalar
            // function
            List fnArgs = new ArrayList(fn.getArgumentCount());
            for (int i = 0, I = fn.getArgumentCount();  i < I; i++) {
                Object arg = fn.getArgument(i);
                fnArgs.add(i, arg); // Keep original selectables
                if (arg instanceof ConcreteFunction) {
                    ConcreteFunction innerFn = (ConcreteFunction) arg;
                    Object val = evaluateAggregateFunction(dec, innerFn, rows);
                    fn.setArgument(i, new Literal(val, ((ConcreteFunction) arg).getDataType()));
                }
            }
            
            if(dec.getRow() == null) {
                dec.setRow(rows.first());
            }
            Object val = fn.evaluate(dec);
            for (int i = 0, I = fn.getArgumentCount();  i < I; i++) {
                fn.setArgument(i, (Selectable) fnArgs.get(i)); // Reset func argument
            }
            return val;
        }
    }

    private ComparatorChain generateOrderChain() {
        ComparatorChain chain = new ComparatorChain();
        for (int i = 0, I = _groupByCols.size(); i < I; i++) {
            Selectable sel = (Selectable) _groupByCols.get(i);
            if (isDescending(sel, _orderByNodes)) {
                chain.setReverseSort(i);
            }
            chain.addComparator(new RowComparator(sel, new RowDecorator(_colIdToFieldMap)));
        }
        return chain;
    }

    private boolean isAggregateFunction(Object sel) {
        if (sel instanceof ConcreteFunction) {
            FindAggregateFunctionVisitor findAggr = new FindAggregateFunctionVisitor();
            findAggr.visit((Selectable) sel); // Check for aggregate functions
            if (findAggr.foundAggregateFunction()) {
                return true;
            }
        }
        return false;
    }

    private boolean isDescending(Selectable sel, List orderNodes) {
        if (null != orderNodes && null != sel) {
            for (Iterator iter = orderNodes.iterator(); iter.hasNext();) {
                OrderNode node = (OrderNode) (iter.next());
                if (node.getSelectable().equals(sel)) {
                    return node.isDescending();
                }
            }
        }
        return false;
    }

    private boolean isEmptyGroupBy() {
        return (_groupByCols == null || _groupByCols.isEmpty());
    }

    private Row makeGroupRow(List currGroup, RowDecorator dec, Row row) throws AxionException {
        List myCurrGroup = new ArrayList(currGroup);
        RowIterator currGroupRowIter = new ListRowIterator(myCurrGroup);

        dec.setRow(row);
        _aggrFnValueCache.clear();
        if (acceptable(currGroupRowIter, dec)) {
            return makeGroupRow(currGroupRowIter, dec);
        }
        return null;
    }

    private Row makeGroupRow(RowIterator currGroupRowIter, RowDecorator dec) throws AxionException {
        SimpleRow rowOut = new SimpleRow(_selected.size());
        for (int i = 0, I = _selected.size(); i < I; i++) {
            if (_isAggregateFunction[i]) {
                rowOut.set(i, evaluateAggregateFunction(dec, (ConcreteFunction) _selected.get(i), currGroupRowIter));
            } else {
                rowOut.set(i, ((Selectable) _selected.get(i)).evaluate(dec));
            }
        }
        return rowOut;
    }

    // This assumes rows are sorted.
    private List makeGroupRows(RowIterator rows) throws AxionException {
        if (_groupByCols != null && !_groupByCols.isEmpty() && !rows.hasNext()) {
            return Collections.EMPTY_LIST;
        }

        List comparators = new ArrayList();
        if (_groupByCols != null && !_groupByCols.isEmpty()) {
            RowDecorator dec = new RowDecorator(_colIdToFieldMap);
            for (int i = 0,  I = _groupByCols.size(); i < I; i++) {
                comparators.add(new RowComparator((Selectable) _groupByCols.get(i), dec));
            }
        }

        List groupedRows = new ArrayList();
        ArrayList currGroup = new ArrayList();
        RowDecorator dec = new RowDecorator(_colIdToFieldMap);

        Row lastRow = null;
        if (rows.hasNext() && !comparators.isEmpty()) {
            lastRow = rows.next();
            addToCurrentGroup(dec, lastRow, currGroup);
            while (rows.hasNext()) {
                Row row = rows.next();
                for (int i = 0, I = comparators.size(); i < I && !currGroup.isEmpty(); i++) {
                    RowComparator comp = (RowComparator) comparators.get(i);

                    // Once we have a group we can apply group by aggregate function
                    if (comp.compare(lastRow, row) != 0) {
                        Row groupdRow = makeGroupRow(currGroup, dec, lastRow);
                        if (groupdRow != null) {
                            groupedRows.add(groupdRow);
                        }
                        currGroup.clear();
                        break;
                    }
                }
                addToCurrentGroup(dec, row, currGroup);
                lastRow = row;
            }
            // Make group row if any pending group exists.
            Row groupdRow = makeGroupRow(currGroup, dec, lastRow);
            if (groupdRow != null) {
                groupedRows.add(groupdRow);
            }
        } else {
            groupedRows.add(makeGroupRow(rows, dec));
        }
        
        return groupedRows;
    }

    private List makeGroupRows(SortedMap groupedRowMap) throws AxionException {
        if (_groupByCols != null && groupedRowMap.isEmpty()) {
            return Collections.EMPTY_LIST;
        }

        List groupedRows = new ArrayList();
        List currGroup = new ArrayList();
        RowDecorator dec = new RowDecorator(_colIdToFieldMap);

        Iterator iter = groupedRowMap.values().iterator();
        while (iter.hasNext()) {
            currGroup = (List) iter.next();
            Row groupdRow = makeGroupRow(currGroup, dec, (Row) currGroup.get(0));
            if (groupdRow != null) {
                groupedRows.add(groupdRow);
            }
        }
        return groupedRows;
    }

    private Map _aggrFnValueCache;
    private Map _colIdToFieldMap;
    private List _groupByCols;
    private Selectable _having;
    private boolean[] _isAggregateFunction;
    private List _orderByNodes;
    private List _selected;
    private Selectable _where;
    private boolean _preSorted = true;
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.