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