001/*
002 * Copyright (c) 2016-2017 Chris K Wensel <chris@wensel.net>. All Rights Reserved.
003 * Copyright (c) 2007-2017 Xplenty, Inc. All Rights Reserved.
004 *
005 * Project and contact information: http://www.cascading.org/
006 *
007 * This file is part of the Cascading project.
008 *
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *     http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021
022package cascading.operation;
023
024import java.beans.ConstructorProperties;
025import java.lang.reflect.Type;
026import java.util.Arrays;
027
028import cascading.flow.FlowProcess;
029import cascading.tuple.Fields;
030import cascading.tuple.Tuple;
031import cascading.tuple.TupleEntryCollector;
032import cascading.util.Util;
033
034/**
035 * The Identity function simply passes incoming arguments back out again. Optionally argument fields can be renamed, and/or
036 * coerced into specific types.
037 * <p>
038 * During coercion, if the given type is a primitive ({@code long}), and the tuple value is null, {@code 0} is returned.
039 * If the type is an Object ({@code java.lang.Long}), and the tuple value is {@code null}, {@code null} is returned.
040 */
041public class Identity extends BaseOperation<Identity.Functor> implements Function<Identity.Functor>
042  {
043  /** Field types */
044  private Type[] types = null;
045
046  /**
047   * Constructor Identity creates a new Identity instance that will pass the argument values to its output. Use this
048   * constructor for a simple copy Pipe.
049   */
050  public Identity()
051    {
052    super( Fields.ARGS );
053    }
054
055  /**
056   * Constructor Identity creates a new Identity instance that will coerce the values to the give types.
057   *
058   * @param types of type Class...
059   */
060  @ConstructorProperties({"types"})
061  public Identity( Class... types )
062    {
063    super( Fields.ARGS );
064
065    if( types.length == 0 )
066      throw new IllegalArgumentException( "number of types must not be zero" );
067
068    this.types = Arrays.copyOf( types, types.length );
069    }
070
071  /**
072   * Constructor Identity creates a new Identity instance that will rename the argument fields to the given
073   * fieldDeclaration.
074   *
075   * @param fieldDeclaration of type Fields
076   */
077  @ConstructorProperties({"fieldDeclaration"})
078  public Identity( Fields fieldDeclaration )
079    {
080    super( fieldDeclaration ); // don't need to set size, default is ANY
081
082    this.types = fieldDeclaration.getTypes();
083    }
084
085  /**
086   * Constructor Identity creates a new Identity instance that will rename the argument fields to the given
087   * fieldDeclaration, and coerce the values to the give types.
088   *
089   * @param fieldDeclaration of type Fields
090   * @param types            of type Class...
091   */
092  @ConstructorProperties({"fieldDeclaration", "types"})
093  public Identity( Fields fieldDeclaration, Class... types )
094    {
095    super( fieldDeclaration );
096    this.types = Arrays.copyOf( types, types.length );
097
098    if( !fieldDeclaration.isSubstitution() && fieldDeclaration.size() != types.length )
099      throw new IllegalArgumentException( "fieldDeclaration and types must be the same size" );
100    }
101
102  public Type[] getTypes()
103    {
104    return Util.copy( types );
105    }
106
107  @Override
108  public void prepare( FlowProcess flowProcess, OperationCall<Functor> operationCall )
109    {
110    Functor functor;
111
112    if( types != null )
113      {
114      Tuple result = Tuple.size( types.length );
115
116      functor = functionCall ->
117      {
118      TupleEntryCollector outputCollector = functionCall.getOutputCollector();
119
120      outputCollector.add( functionCall.getArguments().getCoercedTuple( types, result ) );
121      };
122      }
123    else
124      {
125      functor = functionCall ->
126      {
127      TupleEntryCollector outputCollector = functionCall.getOutputCollector();
128
129      outputCollector.add( functionCall.getArguments().getTuple() );
130      };
131
132      operationCall.setContext( functor );
133      }
134
135    operationCall.setContext( functor );
136    }
137
138  @Override
139  public void operate( FlowProcess flowProcess, FunctionCall<Functor> functionCall )
140    {
141    functionCall.getContext().operate( functionCall );
142    }
143
144  @Override
145  public boolean equals( Object object )
146    {
147    if( this == object )
148      return true;
149    if( !( object instanceof Identity ) )
150      return false;
151    if( !super.equals( object ) )
152      return false;
153
154    Identity identity = (Identity) object;
155
156    if( !Arrays.equals( types, identity.types ) )
157      return false;
158
159    return true;
160    }
161
162  @Override
163  public int hashCode()
164    {
165    int result = super.hashCode();
166    result = 31 * result + ( types != null ? Arrays.hashCode( types ) : 0 );
167    return result;
168    }
169
170  protected interface Functor
171    {
172    void operate( FunctionCall<Functor> functionCall );
173    }
174  }