    001/*
    002 * Copyright (c) 2007-2016 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
    021package cascading.tuple;
    022
    023import java.beans.ConstructorProperties;
    024import java.lang.reflect.Type;
    025import java.util.Arrays;
    026import java.util.Iterator;
    027
    028import cascading.tuple.coerce.Coercions;
    029import cascading.tuple.type.CoercibleType;
    030import cascading.util.ForeverValueIterator;
    031
    032/**
    033 * Class TupleEntry allows a {@link Tuple} instance and its declaring {@link Fields} instance to be used as a single object.
    034 * <p/>
    035 * Once a TupleEntry is created, its Fields cannot be changed, but the Tuple instance it holds can be replaced or
    036 * modified. The managed Tuple should not have elements added or removed, as this will break the relationship with
    037 * the associated Fields instance.
    038 * <p/>
    039 * If type information is provided on the Fields instance, all setters on this class will use that information to
    040 * coerce the given object to the expected type.
    041 * <p/>
    042 * For example, if position is is of type {@code long}, then {@code entry.setString(0, "9" )} will coerce the "9" to a
    043 * long {@code 9}. Thus, {@code entry.getObject(0) == 9l}.
    044 * <p/>
    045 * No coercion is performed with the {@link #getObject(Comparable)} and {@link #getObject(int)} methods.
    046 * <p/>
    047 * To set a value without coercion, see the {@link #setRaw(Comparable, Object)} and {@link #setRaw(int, Object)}
    048 * methods.
    049 *
    050 * @see Fields
    051 * @see Tuple
    052 */
    053public class TupleEntry
    054  {
    055  private static final CoercibleType[] EMPTY_COERCIONS = new CoercibleType[ 0 ];
    056  private static final ForeverValueIterator<CoercibleType> OBJECT_ITERATOR = new ForeverValueIterator<CoercibleType>( Coercions.OBJECT );
    057
    058  /** An EMPTY TupleEntry instance for use as a stand in instead of a {@code null}. */
    059  public static final TupleEntry NULL = new TupleEntry( Fields.NONE, Tuple.NULL );
    060
    061  /** Field fields */
    062  private Fields fields;
    063
    064  private CoercibleType[] coercions = EMPTY_COERCIONS;
    065
    066  /** Field isUnmodifiable */
    067  private boolean isUnmodifiable = false;
    068  /** Field tuple */
    069  Tuple tuple;
    070
    071  /**
    072   * Method select will select a new Tuple instance from the given set of entries. Entries order is significant to
    073   * the selector.
    074   *
    075   * @param selector of type Fields
    076   * @param entries  of type TupleEntry
    077   * @return Tuple
    078   */
    079  public static Tuple select( Fields selector, TupleEntry... entries )
    080    {
    081    // todo: consider just appending tuples values and just peeking those values
    082    Tuple result = null;
    083
    084    // does not do field checks
    085    if( selector.isAll() )
    086      {
    087      for( TupleEntry entry : entries )
    088        {
    089        if( result == null )
    090          result = entry.getTuple();
    091        else
    092          result = result.append( entry.getTuple() );
    093        }
    094
    095      return result;
    096      }
    097
    098    int size = 0;
    099
    100    for( TupleEntry entry : entries )
    101      size += entry.size();
    102
    103    result = Tuple.size( selector.size() );
    104
    105    int offset = 0;
    106
    107    for( TupleEntry entry : entries )
    108      {
    109      for( int i = 0; i < selector.size(); i++ )
    110        {
    111        Comparable field = selector.get( i );
    112
    113        int pos;
    114
    115        if( field instanceof String )
    116          {
    117          pos = entry.fields.indexOfSafe( field );
    118
    119          if( pos == -1 )
    120            continue;
    121          }
    122        else
    123          {
    124          pos = entry.fields.translatePos( (Integer) field, size ) - offset;
    125
    126          if( pos >= entry.size() || pos < 0 )
    127            continue;
    128          }
    129
    130        result.set( i, entry.getObject( pos ) ); // last in wins
    131        }
    132
    133      offset += entry.size();
    134      }
    135
    136    return result;
    137    }
    138
    139  /** Constructor TupleEntry creates a new TupleEntry instance. */
    140  public TupleEntry()
    141    {
    142    this.fields = Fields.NONE;
    143
    144    setCoercions();
    145    }
    146
    147  /**
    148   * Constructor TupleEntry creates a new TupleEntry instance.
    149   *
    150   * @param isUnmodifiable of type boolean
    151   */
    152  @ConstructorProperties({"isUnmodifiable"})
    153  public TupleEntry( boolean isUnmodifiable )
    154    {
    155    this.fields = Fields.NONE;
    156    this.isUnmodifiable = isUnmodifiable;
    157
    158    setCoercions();
    159    }
    160
    161  /**
    162   * Constructor TupleEntry creates a new TupleEntry instance.
    163   *
    164   * @param fields of type Fields
    165   */
    166  @ConstructorProperties({"fields"})
    167  public TupleEntry( Fields fields )
    168    {
    169    this.fields = fields;
    170
    171    setCoercions();
    172    }
    173
    174  /**
    175   * Constructor TupleEntry creates a new TupleEntry instance.
    176   *
    177   * @param fields         of type Fields
    178   * @param isUnmodifiable of type boolean
    179   */
    180  @ConstructorProperties({"fields", "isUnmodifiable"})
    181  public TupleEntry( Fields fields, boolean isUnmodifiable )
    182    {
    183    this.fields = fields;
    184    this.isUnmodifiable = isUnmodifiable;
    185
    186    setCoercions();
    187    }
    188
    189  /**
    190   * Constructor TupleEntry creates a new TupleEntry instance.
    191   *
    192   * @param fields         of type Fields
    193   * @param tuple          of type Tuple
    194   * @param isUnmodifiable of type boolean
    195   */
    196  @ConstructorProperties({"fields", "tuple", "isUnmodifiable"})
    197  public TupleEntry( Fields fields, Tuple tuple, boolean isUnmodifiable )
    198    {
    199    this.fields = fields;
    200    this.isUnmodifiable = isUnmodifiable;
    201    setTuple( tuple );
    202
    203    setCoercions();
    204    }
    205
    206  /**
    207   * Constructor TupleEntry creates a new TupleEntry instance.
    208   *
    209   * @param fields of type Fields
    210   * @param tuple  of type Tuple
    211   */
    212  @ConstructorProperties({"fields", "tuple"})
    213  public TupleEntry( Fields fields, Tuple tuple )
    214    {
    215    this.fields = fields;
    216    this.tuple = tuple;
    217
    218    setCoercions();
    219    }
    220
    221  /**
    222   * Constructor TupleEntry creates a new TupleEntry instance that is a safe copy of the given tupleEntry.
    223   * <p/>
    224   * The new instance is safe to cache and will be modifiable regardless of the given tupleEntry state.
    225   *
    226   * @param tupleEntry of type TupleEntry
    227   */
    228  @ConstructorProperties({"tupleEntry"})
    229  public TupleEntry( TupleEntry tupleEntry )
    230    {
    231    this.fields = tupleEntry.getFields();
    232    this.tuple = tupleEntry.getTupleCopy();
    233
    234    setCoercions();
    235    }
    236
    237  /**
    238   * Constructor TupleEntry creates a new TupleEntry instance.
    239   *
    240   * @param tuple of type Tuple
    241   */
    242  @ConstructorProperties({"tuple"})
    243  public TupleEntry( Tuple tuple )
    244    {
    245    this.fields = Fields.size( tuple.size() );
    246    this.tuple = tuple;
    247
    248    setCoercions();
    249    }
    250
    251  private void setCoercions()
    252    {
    253    if( coercions != EMPTY_COERCIONS )
    254      return;
    255
    256    coercions = getCoercions( getFields(), tuple );
    257    }
    258
    259  static CoercibleType[] getCoercions( Fields fields, Tuple tuple )
    260    {
    261    Type[] types = fields.types; // safe to not get a copy
    262    int size = fields.size();
    263
    264    size = size == 0 && tuple != null ? tuple.size() : size;
    265
    266    if( size == 0 )
    267      return EMPTY_COERCIONS;
    268
    269    return Coercions.coercibleArray( size, types );
    270    }
    271
    272  /**
    273   * Method isUnmodifiable returns true if this TupleEntry is unmodifiable.
    274   *
    275   * @return boolean
    276   */
    277  public boolean isUnmodifiable()
    278    {
    279    return isUnmodifiable;
    280    }
    281
    282  /**
    283   * Method getFields returns the fields of this TupleEntry object.
    284   *
    285   * @return the fields (type Fields) of this TupleEntry object.
    286   */
    287  public Fields getFields()
    288    {
    289    return fields;
    290    }
    291
    292  /**
    293   * Returns true if there are types associated with this instance.
    294   *
    295   * @return boolean
    296   */
    297  public boolean hasTypes()
    298    {
    299    return fields.hasTypes();
    300    }
    301
    302  /**
    303   * Method getTuple returns the tuple of this TupleEntry object.
    304   *
    305   * @return the tuple (type Tuple) of this TupleEntry object.
    306   */
    307  public Tuple getTuple()
    308    {
    309    return tuple;
    310    }
    311
    312  /**
    313   * Method getTupleCopy returns a copy of the tuple of this TupleEntry object.
    314   *
    315   * @return a copy of the tuple (type Tuple) of this TupleEntry object.
    316   */
    317  public Tuple getTupleCopy()
    318    {
    319    return new Tuple( tuple );
    320    }
    321
    322  /**
    323   * Method getCoercedTuple is a helper method for copying the current tuple elements into a new Tuple,
    324   * of the same size, as the requested coerced types.
    325   *
    326   * @param types of type Type[]
    327   * @return returns the a new Tuple instance with coerced values
    328   */
    329  public Tuple getCoercedTuple( Type[] types )
    330    {
    331    return getCoercedTuple( types, Tuple.size( types.length ) );
    332    }
    333
    334  /**
    335   * Method getCoercedTuple is a helper method for copying the current tuple elements into the new Tuple,
    336   * of the same size, as the requested coerced types.
    337   *
    338   * @param types of type Type[]
    339   * @param into  of type Tuple
    340   * @return returns the given into Tuple instance with coerced values
    341   */
    342  public Tuple getCoercedTuple( Type[] types, Tuple into )
    343    {
    344    if( into == null )
    345      throw new IllegalArgumentException( "into argument Tuple may not be null" );
    346
    347    if( coercions.length != types.length || types.length != into.size() )
    348      throw new IllegalArgumentException( "current entry and given tuple and types must be same length" );
    349
    350    for( int i = 0; i < coercions.length; i++ )
    351      {
    352      Object element = tuple.getObject( i );
    353      into.set( i, Coercions.coerce( coercions[ i ], element, types[ i ] ) );
    354      }
    355
    356    return into;
    357    }
    358
    359  /**
    360   * Method setTuple sets the tuple of this TupleEntry object, no copy will be performed.
    361   * <p/>
    362   * If the given tuple is "unmodifiable" ({@code Tuple.isUnmodifiable() == true}) and this TupleEntry is
    363   * not "unmodifiable", an exception will be thrown.
    364   * <p/>
    365   * Unmodifiable tuples are generally owned by the system and cannot be be changed and must not be cached.
    366   *
    367   * @param tuple the tuple of this TupleEntry object.
    368   */
    369  public void setTuple( Tuple tuple )
    370    {
    371    if( !isUnmodifiable && tuple != null && tuple.isUnmodifiable() )
    372      throw new IllegalArgumentException( "current entry is modifiable but given tuple is not modifiable, make copy of given Tuple first" );
    373
    374    if( tuple != null && isUnmodifiable )
    375      this.tuple = Tuples.asUnmodifiable( tuple );
    376    else
    377      this.tuple = tuple;
    378
    379    setCoercions();
    380    }
    381
    382  /**
    383   * Method setCanonicalTuple replaces each value of the current tuple with the given tuple elements after
    384   * they are coerced.
    385   * <p/>
    386   * This method will modify the existing Tuple wrapped by this TupleEntry instance even
    387   * if it is marked as unmodifiable.
    388   * <p/>
    389   * If tuple argument is {@code null}, the current tuple will be set to {@code null}.
    390   *
    391   * @param tuple to replace the current wrapped Tuple instance
    392   */
    393  public void setCanonicalTuple( Tuple tuple )
    394    {
    395    if( tuple == null )
    396      {
    397      this.tuple = null;
    398      return;
    399      }
    400
    401    if( isUnmodifiable )
    402      tuple = Tuples.asUnmodifiable( tuple );
    403
    404    if( fields.size() != tuple.size() )
    405      throw new IllegalArgumentException( "current entry and given tuple must be same length" );
    406
    407    for( int i = 0; i < coercions.length; i++ )
    408      {
    409      Object element = tuple.getObject( i );
    410
    411      this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
    412      }
    413    }
    414
    415  /**
    416   * Method setCanonicalValues replaces each value of the current tuple with th give Object[]
    417   * after they are coerced.
    418   *
    419   * @param values to replace the current wrapped tuple instance values
    420   */
    421  public void setCanonicalValues( Object[] values )
    422    {
    423    if( fields.size() != values.length )
    424      throw new IllegalArgumentException( "current entry and given array must be same length" );
    425
    426    for( int i = 0; i < coercions.length; i++ )
    427      {
    428      Object element = values[ i ];
    429
    430      this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
    431      }
    432    }
    433
    434  /**
    435   * Method size returns the number of values in this instance.
    436   *
    437   * @return int
    438   */
    439  public int size()
    440    {
    441    return tuple.size();
    442    }
    443
    444  /**
    445   * Method getObject returns the value in the given position pos.
    446   * <p/>
    447   * No coercion is performed if there is an associated coercible type.
    448   *
    449   * @param pos position of the element to return.
    450   * @return Object
    451   */
    452  public Object getObject( int pos )
    453    {
    454    return tuple.getObject( pos );
    455    }
    456
    457  /**
    458   * Method getObject returns the value in the given field or position as the requested type.
    459   * <p/>
    460   * Coercion is performed to the given type.
    461   *
    462   * @param pos position of the element to return.
    463   * @return Object
    464   */
    465  public Object getObject( int pos, Type type )
    466    {
    467    return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
    468    }
    469
    470  /**
    471   * Method getObject returns the value in the given field or position.
    472   * <br/>
    473   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    474   * be considered.
    475   * <p/>
    476   * No coercion is performed if there is an associated coercible type.
    477   *
    478   * @param fieldName field name or position to return
    479   * @return Comparable
    480   */
    481  public Object getObject( Comparable fieldName )
    482    {
    483    int pos = fields.getPos( asFieldName( fieldName ) );
    484    return tuple.getObject( pos );
    485    }
    486
    487  /**
    488   * Method getObject returns the value in the given field or position as the requested type.
    489   * <br/>
    490   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    491   * be considered.
    492   * <p/>
    493   * Coercion is performed to the given type.
    494   *
    495   * @param fieldName field name or position to return
    496   * @return Comparable
    497   */
    498  public Object getObject( Comparable fieldName, Type type )
    499    {
    500    int pos = fields.getPos( asFieldName( fieldName ) );
    501    return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
    502    }
    503
    504  /**
    505   * Method set sets the value in the given position.
    506   * <p/>
    507   * No coercion is performed if there is an associated coercible type.
    508   *
    509   * @param pos   position to set
    510   * @param value of type Comparable
    511   */
    512  public void setRaw( int pos, Object value )
    513    {
    514    tuple.set( pos, value );
    515    }
    516
    517  /**
    518   * Method set sets the value in the given field or position.
    519   * <p/>
    520   * No coercion is performed if there is an associated coercible type.
    521   *
    522   * @param fieldName field name or position to set
    523   * @param value     of type Comparable
    524   */
    525  public void setRaw( Comparable fieldName, Object value )
    526    {
    527    tuple.set( fields.getPos( asFieldName( fieldName ) ), value );
    528    }
    529
    530  /**
    531   * Method set sets the value in the given field or position.
    532   *
    533   * @param fieldName field name or position to set
    534   * @param value     of type Comparable
    535   */
    536  public void setObject( Comparable fieldName, Object value )
    537    {
    538    int pos = fields.getPos( asFieldName( fieldName ) );
    539
    540    tuple.set( pos, coercions[ pos ].canonical( value ) );
    541    }
    542
    543  /**
    544   * Method setBoolean sets the value in the given field or position.
    545   *
    546   * @param fieldName field name or position to set
    547   * @param value     of type boolean
    548   */
    549  public void setBoolean( Comparable fieldName, boolean value )
    550    {
    551    int pos = fields.getPos( asFieldName( fieldName ) );
    552
    553    tuple.set( pos, coercions[ pos ].canonical( value ) );
    554    }
    555
    556  /**
    557   * Method setShort sets the value in the given field or position.
    558   *
    559   * @param fieldName field name or position to set
    560   * @param value     of type short
    561   */
    562  public void setShort( Comparable fieldName, short value )
    563    {
    564    int pos = fields.getPos( asFieldName( fieldName ) );
    565
    566    tuple.set( pos, coercions[ pos ].canonical( value ) );
    567    }
    568
    569  /**
    570   * Method setInteger sets the value in the given field or position.
    571   *
    572   * @param fieldName field name or position to set
    573   * @param value     of type int
    574   */
    575  public void setInteger( Comparable fieldName, int value )
    576    {
    577    int pos = fields.getPos( asFieldName( fieldName ) );
    578
    579    tuple.set( pos, coercions[ pos ].canonical( value ) );
    580    }
    581
    582  /**
    583   * Method setLong sets the value in the given field or position.
    584   *
    585   * @param fieldName field name or position to set
    586   * @param value     of type long
    587   */
    588  public void setLong( Comparable fieldName, long value )
    589    {
    590    int pos = fields.getPos( asFieldName( fieldName ) );
    591
    592    tuple.set( pos, coercions[ pos ].canonical( value ) );
    593    }
    594
    595  /**
    596   * Method setFloat sets the value in the given field or position.
    597   *
    598   * @param fieldName field name or position to set
    599   * @param value     of type float
    600   */
    601  public void setFloat( Comparable fieldName, float value )
    602    {
    603    int pos = fields.getPos( asFieldName( fieldName ) );
    604
    605    tuple.set( pos, coercions[ pos ].canonical( value ) );
    606    }
    607
    608  /**
    609   * Method setDouble sets the value in the given field or position.
    610   *
    611   * @param fieldName field name or position to set
    612   * @param value     of type double
    613   */
    614  public void setDouble( Comparable fieldName, double value )
    615    {
    616    int pos = fields.getPos( asFieldName( fieldName ) );
    617
    618    tuple.set( pos, coercions[ pos ].canonical( value ) );
    619    }
    620
    621  /**
    622   * Method setString sets the value in the given field or position.
    623   *
    624   * @param fieldName field name or position to set
    625   * @param value     of type String
    626   */
    627  public void setString( Comparable fieldName, String value )
    628    {
    629    int pos = fields.getPos( asFieldName( fieldName ) );
    630    tuple.set( pos, coercions[ pos ].canonical( value ) );
    631    }
    632
    633  /**
    634   * Method getString returns the element for the given field name or position as a String.
    635   * <br/>
    636   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    637   * be considered.
    638   *
    639   * @param fieldName field name or position to return
    640   * @return String
    641   */
    642  public String getString( Comparable fieldName )
    643    {
    644    return (String) getObject( fieldName, String.class );
    645    }
    646
    647  /**
    648   * Method getFloat returns the element for the given field name or position as a float. Zero if null.
    649   * <br/>
    650   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    651   * be considered.
    652   *
    653   * @param fieldName field name or position to return
    654   * @return float
    655   */
    656  public float getFloat( Comparable fieldName )
    657    {
    658    return (Float) getObject( fieldName, float.class );
    659    }
    660
    661  /**
    662   * Method getDouble returns the element for the given field name or position as a double. Zero if null.
    663   * <br/>
    664   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    665   * be considered.
    666   *
    667   * @param fieldName field name or position to return
    668   * @return double
    669   */
    670  public double getDouble( Comparable fieldName )
    671    {
    672    return (Double) getObject( fieldName, double.class );
    673    }
    674
    675  /**
    676   * Method getInteger  returns the element for the given field name or position as an int. Zero if null.
    677   * <br/>
    678   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    679   * be considered.
    680   *
    681   * @param fieldName field name or position to return
    682   * @return int
    683   */
    684  public int getInteger( Comparable fieldName )
    685    {
    686    return (Integer) getObject( fieldName, int.class );
    687    }
    688
    689  /**
    690   * Method getLong returns the element for the given field name or position as a long. Zero if null.
    691   * <br/>
    692   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    693   * be considered.
    694   *
    695   * @param fieldName field name or position to return
    696   * @return long
    697   */
    698  public long getLong( Comparable fieldName )
    699    {
    700    return (Long) getObject( fieldName, long.class );
    701    }
    702
    703  /**
    704   * Method getShort returns the element for the given field name or position as a short. Zero if null.
    705   * <br/>
    706   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    707   * be considered.
    708   *
    709   * @param fieldName field name or position to return
    710   * @return short
    711   */
    712  public short getShort( Comparable fieldName )
    713    {
    714    return (Short) getObject( fieldName, short.class );
    715    }
    716
    717  /**
    718   * Method getBoolean returns the element for the given field name or position as a boolean.
    719   * If the value is (case ignored) the string 'true', a {@code true} value will be returned. {@code false} if null.
    720   * <br/>
    721   * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    722   * be considered.
    723   *
    724   * @param fieldName field name or position to return
    725   * @return boolean
    726   */
    727  public boolean getBoolean( Comparable fieldName )
    728    {
    729    return (Boolean) getObject( fieldName, boolean.class );
    730    }
    731
    732  private Comparable asFieldName( Comparable fieldName )
    733    {
    734    return Fields.asFieldName( fieldName );
    735    }
    736
    737  /**
    738   * Method selectEntry selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the
    739   * same fields as declared are given, {@code this} will be returned.
    740   * <p/>
    741   * The returned TupleEntry will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance.
    742   * <p/>
    743   * See {@link #selectEntryCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection.
    744   * <p/>
    745   * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent
    746   * on the given selector.
    747   *
    748   * @param selector Fields selector that selects the values to return
    749   * @return TupleEntry
    750   */
    751  public TupleEntry selectEntry( Fields selector )
    752    {
    753    if( selector == null || selector.isAll() || fields == selector ) // == is intentional
    754      return this;
    755
    756    if( selector.isNone() )
    757      return isUnmodifiable ? TupleEntry.NULL : new TupleEntry();
    758
    759    return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ), isUnmodifiable );
    760    }
    761
    762  /**
    763   * Method selectEntry selects the fields specified in selector from this instance.
    764   * <p/>
    765   * It is guaranteed to return a new modifiable TupleEntry instance at a cost of copying data.
    766   * <p/>
    767   * The returned instance is safe to cache.
    768   *
    769   * @param selector Fields selector that selects the values to return
    770   * @return TupleEntry
    771   */
    772  public TupleEntry selectEntryCopy( Fields selector )
    773    {
    774    if( selector == null || selector.isAll() || fields == selector ) // == is intentional
    775      return new TupleEntry( this );
    776
    777    if( selector.isNone() )
    778      return new TupleEntry();
    779
    780    return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ) );
    781    }
    782
    783  /**
    784   * Method selectTuple selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the
    785   * same fields as declared are given, {@code this.getTuple()} will be returned.
    786   * <p/>
    787   * The returned Tuple will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance.
    788   * <p/>
    789   * See {@link #selectTupleCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection.
    790   * <p/>
    791   * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent
    792   * on the given selector.
    793   *
    794   * @param selector Fields selector that selects the values to return
    795   * @return Tuple
    796   */
    797  public Tuple selectTuple( Fields selector )
    798    {
    799    if( selector == null || selector.isAll() || fields == selector ) // == is intentional
    800      return this.tuple;
    801
    802    if( selector.isNone() )
    803      return Tuple.NULL;
    804
    805    Tuple result = tuple.get( fields, selector );
    806
    807    if( isUnmodifiable )
    808      Tuples.asUnmodifiable( result );
    809
    810    return result;
    811    }
    812
    813  /**
    814   * Method selectTupleCopy selects the fields specified in selector from this instance.
    815   * <p/>
    816   * It is guaranteed to return a new modifiable Tuple instance at a cost of copying data.
    817   * <p/>
    818   * The returned instance is safe to cache.
    819   *
    820   * @param selector Fields selector that selects the values to return
    821   * @return Tuple
    822   */
    823  public Tuple selectTupleCopy( Fields selector )
    824    {
    825    if( selector == null || selector.isAll() || fields == selector ) // == is intentional
    826      return new Tuple( this.tuple );
    827
    828    if( selector.isNone() )
    829      return new Tuple();
    830
    831    return tuple.get( fields, selector );
    832    }
    833
    834  /**
    835   * Method selectInto selects the fields specified in the selector from this instance and copies
    836   * them into the given tuple argument.
    837   *
    838   * @param selector of type Fields
    839   * @param tuple    of type Tuple
    840   * @return returns the given tuple argument with new values added
    841   */
    842  public Tuple selectInto( Fields selector, Tuple tuple )
    843    {
    844    if( selector.isNone() )
    845      return tuple;
    846
    847    int[] pos = this.tuple.getPos( fields, selector );
    848
    849    if( pos == null || pos.length == 0 )
    850      {
    851      tuple.addAll( this.tuple );
    852      }
    853    else
    854      {
    855      for( int i : pos )
    856        tuple.add( this.tuple.getObject( i ) );
    857      }
    858
    859    return tuple;
    860    }
    861
    862  /**
    863   * Method setTuple sets the values specified by the selector to the values given by the given tuple, the given
    864   * values will always be copied into this TupleEntry.
    865   *
    866   * @param selector of type Fields
    867   * @param tuple    of type Tuple
    868   */
    869  public void setTuple( Fields selector, Tuple tuple )
    870    {
    871    if( selector == null || selector.isAll() )
    872      this.tuple.setAll( tuple );
    873    else
    874      this.tuple.set( fields, selector, tuple );
    875    }
    876
    877  /**
    878   * Method set sets the values from the given tupleEntry into this TupleEntry instance based on the given
    879   * tupleEntry field names.
    880   * <p/>
    881   * If type information is given, each incoming value will be coerced from its canonical type to the current type.
    882   *
    883   * @param tupleEntry of type TupleEntry
    884   */
    885  public void set( TupleEntry tupleEntry )
    886    {
    887    this.tuple.set( fields, tupleEntry.getFields(), tupleEntry.getTuple(), tupleEntry.coercions );
    888    }
    889
    890  /**
    891   * Method appendNew appends the given TupleEntry instance to this instance.
    892   *
    893   * @param entry of type TupleEntry
    894   * @return TupleEntry
    895   */
    896  public TupleEntry appendNew( TupleEntry entry )
    897    {
    898    Fields appendedFields = fields.append( entry.fields.isUnknown() ? Fields.size( entry.tuple.size() ) : entry.fields );
    899    Tuple appendedTuple = tuple.append( entry.tuple );
    900
    901    return new TupleEntry( appendedFields, appendedTuple );
    902    }
    903
    904  @Override
    905  public boolean equals( Object object )
    906    {
    907    if( this == object )
    908      return true;
    909
    910    if( !( object instanceof TupleEntry ) )
    911      return false;
    912
    913    TupleEntry that = (TupleEntry) object;
    914
    915    if( fields != null ? !fields.equals( that.fields ) : that.fields != null )
    916      return false;
    917
    918    // use comparators if in the this side fields instance
    919    if( tuple != null ? fields.compare( tuple, that.tuple ) != 0 : that.tuple != null )
    920      return false;
    921
    922    return true;
    923    }
    924
    925  @Override
    926  public int hashCode()
    927    {
    928    int result = fields != null ? fields.hashCode() : 0;
    929    result = 31 * result + ( tuple != null ? tuple.hashCode() : 0 );
    930    return result;
    931    }
    932
    933  @Override
    934  public String toString()
    935    {
    936    if( fields == null )
    937      return "empty";
    938    else if( tuple == null )
    939      return "fields: " + fields.print();
    940    else
    941      return "fields: " + fields.print() + " tuple: " + tuple.print();
    942    }
    943
    944  /**
    945   * Method asIterableOf returns an {@link Iterable} instance that will coerce all Tuple elements
    946   * into the given {@code type} parameter.
    947   * <p/>
    948   * This method honors any {@link cascading.tuple.type.CoercibleType} instances on the internal
    949   * Fields instance for the specified Tuple element.
    950   *
    951   * @param type of type Class
    952   * @return an Iterable
    953   */
    954  public <T> Iterable<T> asIterableOf( final Class<T> type )
    955    {
    956    return new Iterable<T>()
    957    {
    958    @Override
    959    public Iterator<T> iterator()
    960      {
    961      final Iterator<CoercibleType> coercibleIterator = coercions.length == 0 ?
    962        OBJECT_ITERATOR :
    963        Arrays.asList( coercions ).iterator();
    964
    965      final Iterator valuesIterator = tuple.iterator();
    966
    967      return new Iterator<T>()
    968      {
    969      @Override
    970      public boolean hasNext()
    971        {
    972        return valuesIterator.hasNext();
    973        }
    974
    975      @Override
    976      public T next()
    977        {
    978        Object next = valuesIterator.next();
    979
    980        return (T) coercibleIterator.next().coerce( next, type );
    981        }
    982
    983      @Override
    984      public void remove()
    985        {
    986        valuesIterator.remove();
    987        }
    988      };
    989      }
    990    };
    991    }
    992  }