/* * Copyright (c) 2007-2010 Concurrent, Inc. All Rights Reserved. * * Project and contact information: http://www.cascading.org/ * * This file is part of the Cascading project. * * Cascading is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Cascading 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Cascading. If not, see <http://www.gnu.org/licenses/>. */ package cascading.operation.aggregator; import java.beans.ConstructorProperties; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import cascading.flow.FlowProcess; import cascading.operation.Aggregator; import cascading.operation.AggregatorCall; import cascading.operation.BaseOperation; import cascading.tuple.Fields; import cascading.tuple.Tuple; import cascading.tuple.TupleEntry; /** * Class ExtremaBase is the base class for Max and Min. The unique thing about Max and Min are that they return the original, * un-coerced, argument value, though a coerced version of the argument is used for the comparison. */ public abstract class ExtremaBase extends BaseOperation<ExtremaBase.Context> implements Aggregator<ExtremaBase.Context> { /** Field ignoreValues */ protected final Collection ignoreValues; protected static class Context { Number extrema; Object value; public Context( Number extrema ) { this.extrema = extrema; } public Context reset( Number extrema ) { this.extrema = extrema; this.value = null; return this; } } @ConstructorProperties({"fieldDeclaration"}) public ExtremaBase( Fields fieldDeclaration ) { super( fieldDeclaration ); ignoreValues = null; } @ConstructorProperties({"numArgs", "fieldDeclaration"}) public ExtremaBase( int numArgs, Fields fieldDeclaration ) { super( numArgs, fieldDeclaration ); ignoreValues = null; if( !fieldDeclaration.isSubstitution() && fieldDeclaration.size() != 1 ) throw new IllegalArgumentException( "fieldDeclaration may only declare 1 field, got: " + fieldDeclaration.size() ); } @ConstructorProperties({"fieldDeclaration", "ignoreValues"}) protected ExtremaBase( Fields fieldDeclaration, Object... ignoreValues ) { super( fieldDeclaration ); this.ignoreValues = new HashSet(); Collections.addAll( this.ignoreValues, ignoreValues ); } public void start( FlowProcess flowProcess, AggregatorCall<Context> aggregatorCall ) { if( aggregatorCall.getContext() == null ) aggregatorCall.setContext( new Context( getInitialValue() ) ); else aggregatorCall.getContext().reset( getInitialValue() ); } protected abstract double getInitialValue(); public void aggregate( FlowProcess flowProcess, AggregatorCall<Context> aggregatorCall ) { TupleEntry entry = aggregatorCall.getArguments(); Context context = aggregatorCall.getContext(); Comparable arg = entry.get( 0 ); if( ignoreValues != null && ignoreValues.contains( arg ) ) return; Number rhs = null; if( arg instanceof Number ) rhs = (Number) arg; else rhs = entry.getDouble( 0 ); Number lhs = context.extrema; if( compare( lhs, rhs ) ) { context.value = arg; // keep and return original value context.extrema = rhs; } } protected abstract boolean compare( Number lhs, Number rhs ); public void complete( FlowProcess flowProcess, AggregatorCall<Context> aggregatorCall ) { aggregatorCall.getOutputCollector().add( getResult( aggregatorCall ) ); } protected Tuple getResult( AggregatorCall<Context> aggregatorCall ) { return new Tuple( (Comparable) aggregatorCall.getContext().value ); } @Override public boolean equals( Object object ) { if( this == object ) return true; if( !( object instanceof ExtremaBase ) ) return false; if( !super.equals( object ) ) return false; ExtremaBase that = (ExtremaBase) object; if( ignoreValues != null ? !ignoreValues.equals( that.ignoreValues ) : that.ignoreValues != null ) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + ( ignoreValues != null ? ignoreValues.hashCode() : 0 ); return result; } }