    001    /*
    002     * Copyright (c) 2007-2014 Concurrent, Inc. All Rights Reserved.
    003     *
    004     * Project and contact information: http://www.cascading.org/
    005     *
    006     * This file is part of the Cascading project.
    007     *
    008     * Licensed under the Apache License, Version 2.0 (the "License");
    009     * you may not use this file except in compliance with the License.
    010     * You may obtain a copy of the License at
    011     *
    012     *     http://www.apache.org/licenses/LICENSE-2.0
    013     *
    014     * Unless required by applicable law or agreed to in writing, software
    015     * distributed under the License is distributed on an "AS IS" BASIS,
    016     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017     * See the License for the specific language governing permissions and
    018     * limitations under the License.
    019     */
    020
    021    package cascading.operation;
    022
    023    import java.io.Serializable;
    024
    025    import cascading.flow.FlowProcess;
    026    import cascading.flow.planner.Scope;
    027    import cascading.pipe.Each;
    028    import cascading.pipe.Every;
    029    import cascading.pipe.Pipe;
    030    import cascading.tuple.Fields;
    031    import cascading.tuple.Tuple;
    032    import cascading.util.TraceUtil;
    033    import cascading.util.Traceable;
    034
    035    /**
    036     * Class BaseOperation is the base class of predicate types that are applied to {@link Tuple} streams via
    037     * the {@link Each} or {@link Every} {@link Pipe}.
    038     * </p>
    039     * Specific examples of Operations are {@link Function}, {@link Filter}, {@link Aggregator}, {@link Buffer},
    040     * and {@link Assertion}.
    041     * <p/>
    042     * By default, {@link #isSafe()} returns {@code true}.
    043     */
    044    public abstract class BaseOperation<Context> implements Serializable, Operation<Context>, Traceable
    045      {
    046      /** Field fieldDeclaration */
    047      protected Fields fieldDeclaration;
    048      /** Field numArgs */
    049      protected int numArgs = ANY;
    050      /** Field trace */
    051      protected String trace = TraceUtil.captureDebugTrace( this ); // see TraceUtil.setTrace() to override
    052
    053      // initialize this operation based on its subclass
    054      {
    055      if( this instanceof Filter || this instanceof Assertion )
    056        fieldDeclaration = Fields.ALL;
    057      else
    058        fieldDeclaration = Fields.UNKNOWN;
    059      }
    060
    061      /**
    062       * Constructs a new instance that returns an {@link Fields#UNKNOWN} {@link Tuple} and accepts any number of arguments.
    063       * </p>
    064       * It is a best practice to always declare the field names and number of arguments via one of the other constructors.
    065       */
    066      protected BaseOperation()
    067        {
    068        }
    069
    070      /**
    071       * Constructs a new instance that returns the fields declared in fieldDeclaration and accepts any number of arguments.
    072       *
    073       * @param fieldDeclaration of type Fields
    074       */
    075      protected BaseOperation( Fields fieldDeclaration )
    076        {
    077        this.fieldDeclaration = fieldDeclaration;
    078        verify();
    079        }
    080
    081      /**
    082       * Constructs a new instance that returns an unknown field set and accepts the given numArgs number of arguments.
    083       *
    084       * @param numArgs of type numArgs
    085       */
    086      protected BaseOperation( int numArgs )
    087        {
    088        this.numArgs = numArgs;
    089        verify();
    090        }
    091
    092      /**
    093       * Constructs a new instance that returns the fields declared in fieldDeclaration and accepts numArgs number of arguments.
    094       *
    095       * @param numArgs          of type numArgs
    096       * @param fieldDeclaration of type Fields
    097       */
    098      protected BaseOperation( int numArgs, Fields fieldDeclaration )
    099        {
    100        this.numArgs = numArgs;
    101        this.fieldDeclaration = fieldDeclaration;
    102        verify();
    103        }
    104
    105      /** Validates the state of this instance. */
    106      private final void verify()
    107        {
    108        if( this instanceof Filter && fieldDeclaration != Fields.ALL )
    109          throw new IllegalArgumentException( "fieldDeclaration must be set to Fields.ALL for filter operations" );
    110
    111        if( this instanceof Assertion && fieldDeclaration != Fields.ALL )
    112          throw new IllegalArgumentException( "fieldDeclaration must be set to Fields.ALL for assertion operations" );
    113
    114        if( fieldDeclaration == null )
    115          throw new IllegalArgumentException( "fieldDeclaration may not be null" );
    116
    117        if( numArgs < 0 )
    118          throw new IllegalArgumentException( "numArgs may not be negative" );
    119        }
    120
    121      /** Method prepare does nothing, and may safely be overridden. */
    122      @Override
    123      public void prepare( FlowProcess flowProcess, OperationCall<Context> operationCall )
    124        {
    125        // do nothing
    126        }
    127
    128      @Override
    129      public void flush( FlowProcess flowProcess, OperationCall<Context> contextOperationCall )
    130        {
    131        }
    132
    133      /** Method cleanup does nothing, and may safely be overridden. */
    134      @Override
    135      public void cleanup( FlowProcess flowProcess, OperationCall<Context> operationCall )
    136        {
    137        // do nothing
    138        }
    139
    140      @Override
    141      public Fields getFieldDeclaration()
    142        {
    143        return fieldDeclaration;
    144        }
    145
    146      @Override
    147      public int getNumArgs()
    148        {
    149        return numArgs;
    150        }
    151
    152      @Override
    153      public boolean isSafe()
    154        {
    155        return true;
    156        }
    157
    158      @Override
    159      public String getTrace()
    160        {
    161        return trace;
    162        }
    163
    164      @Override
    165      public String toString()
    166        {
    167        return toStringInternal( (Operation) this );
    168        }
    169
    170      public static String toStringInternal( Operation operation )
    171        {
    172        StringBuilder buffer = new StringBuilder();
    173
    174        Class<? extends Operation> type = operation.getClass();
    175        if( type.getSimpleName().length() != 0 )
    176          buffer.append( type.getSimpleName() );
    177        else
    178          buffer.append( type.getName() ); // should get something for an anonymous inner class
    179
    180        if( operation.getFieldDeclaration() != null )
    181          buffer.append( "[decl:" ).append( operation.getFieldDeclaration() ).append( "]" );
    182
    183        if( operation.getNumArgs() != ANY )
    184          buffer.append( "[args:" ).append( operation.getNumArgs() ).append( "]" );
    185
    186        return buffer.toString();
    187        }
    188
    189      public static void printOperationInternal( Operation operation, StringBuffer buffer, Scope scope )
    190        {
    191        Class<? extends Operation> type = operation.getClass();
    192
    193        if( type.getSimpleName().length() != 0 )
    194          buffer.append( type.getSimpleName() );
    195        else
    196          buffer.append( type.getName() ); // should get something for an anonymous inner class
    197
    198        if( scope.getOperationDeclaredFields() != null )
    199          buffer.append( "[decl:" ).append( scope.getOperationDeclaredFields().printVerbose() ).append( "]" );
    200
    201        if( operation.getNumArgs() != ANY )
    202          buffer.append( "[args:" ).append( operation.getNumArgs() ).append( "]" );
    203        }
    204
    205      @Override
    206      public boolean equals( Object object )
    207        {
    208        if( this == object )
    209          return true;
    210        if( !( object instanceof BaseOperation ) )
    211          return false;
    212
    213        BaseOperation that = (BaseOperation) object;
    214
    215        if( numArgs != that.numArgs )
    216          return false;
    217        if( fieldDeclaration != null ? !fieldDeclaration.equals( that.fieldDeclaration ) : that.fieldDeclaration != null )
    218          return false;
    219
    220        return true;
    221        }
    222
    223      @Override
    224      public int hashCode()
    225        {
    226        int result = fieldDeclaration != null ? fieldDeclaration.hashCode() : 0;
    227        result = 31 * result + numArgs;
    228        return result;
    229        }
    230      }