/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.math.functions;
import xxl.core.collections.bags.Bag;
import xxl.core.predicates.Equal;
import xxl.core.predicates.Predicate;
import xxl.core.predicates.RightBind;
/**
* This class realizes a metadata aggregation-function performing an early
* duplicate elimination during an aggregate operation. <br>
* Using this function in an Aggregator offers the ability
* to perform SQL statements like: <br>
* <pre>
* SELECT DISTINCT SUM(A), DISTINCT AVG(B)
* FROM Table1
* WHERE ...
* </pre>
*
* A DistinctMetaDataAggregateFunction decorates a given MetaDataAggregationFunction
* and and uses a bag to realize the duplicate elimination.
* That means, the elements of the input cursor are inserted in the bag
* and the specified predicate determines during the execution
* of the query() method, if an element in the bag matches
* with the next element, the aggregate should be computed
* with. If no predicate has been specified in a constructor,
* the predicate {@link xxl.core.predicates.Equal} is used as a
* default. <br>
* So the next aggregate, i.e.
* <code>function.invoke(aggregate, next)</code>,
* is only computed, if the bag does not contain the
* next element the aggregate function will be invoked with.
* <p>
* Since a DistinctMetaDataAggregateFunction extends MetaDataAggregationFunction,
* it has to provide meta data information. Therefore
* the meta data information of the given MetaDataFunction
* will be passed during a call to <tt>getMetaData()</tt>.
*
* @param <P> the type of the aggregated values.
* @param <A> the return type of the function, i.e., the type of the aggregate.
* @param <M> the type of the mata data provided by this aggregation-function.
*/
public class DistinctMetaDataAggregationFunction<P, A, M> extends MetaDataAggregationFunction<P, A, M> {
/**
* The bag containing unique elements of the input cursor.
*/
protected Bag<P> bag;
/**
* The predicate used to determine, if an element of the
* bag and the next element, the incremental aggregate
* should be computed with, are equal.
*/
protected Predicate<? super P> predicate;
/**
* Creates a new DistinctMetaDataAggregateFunction.
*
* @param function a MetaDataAggregationFunction computing an incremental aggregate, e.g. SUM, AVG, ...
* @param bag a bag used to store the input cursor's elements.
* @param predicate binary predicate determining if an element of the bag and the next
* element, the incremental aggregate should be computed with, are equal
*/
public DistinctMetaDataAggregationFunction(MetaDataAggregationFunction<P, A, M> function, Bag<P> bag, Predicate<? super P> predicate) {
super(function);
this.bag = bag;
this.predicate = predicate;
}
/**
* Creates a new DistinctMetaDataAggregateFunction.
* Uses a default instance of the predicate {@link xxl.core.predicates.Equal}.
*
* @param function a MetaDataAggregationFunction computing an incremental aggregate, e.g. SUM, AVG, ...
* @param bag a bag used to store the input cursor's elements
*/
public DistinctMetaDataAggregationFunction(MetaDataAggregationFunction<P, A, M> function, Bag<P> bag) {
this(function, bag, Equal.DEFAULT_INSTANCE);
}
/**
* Creates a new DistinctMetaDataAggregateFunction.
* Uses a bag generated by Bag.FACTORY_METHOD.invoke() and
* a default instance of the predicate {@link xxl.core.predicates.Equal}.
*
* @param function a MetaDataFunction computing an incremental aggregate, e.g. SUM, AVG, ...
*/
public DistinctMetaDataAggregationFunction(MetaDataAggregationFunction<P, A, M> function) {
this(function, (Bag)Bag.FACTORY_METHOD.invoke(), Equal.DEFAULT_INSTANCE);
}
/**
* Overrides the invoke method with two parameters used by XXL's incremental
* Aggregators in the packages xxl.core.cursors and xxl.core.relational. <br>
* If the size of the bag is greater than 0, the bag is queried
* in order to check if the <tt>next</tt> element is already contained in
* the bag. If this is the case, the last aggregate is returned.
* Otherwise the <tt>next</tt> element is inserted in the bag and
* the aggregate is incrementally computed as usual.
*
* @param aggregate aggregate
* @param next next element of the input cursor
* @return last aggregate, if <tt>next</tt> is contained in the bag,
* otherwise the new computed aggregate is returned
*/
public A invoke(A aggregate, P next) {
if (bag.size() > 0)
if (bag.query(new RightBind<P>(predicate, next)).hasNext())
return aggregate;
bag.insert(next);
return super.invoke(aggregate, next);
}
/**
* Returns the metadata associated with this aggregation function.
*
* @return the metadata information.
*/
public M getMetaData() {
return ((MetaDataAggregationFunction<P, A, M>)aggregationFunction).getMetaData();
}
}