    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.tuple;
    022
    023    import java.beans.ConstructorProperties;
    024    import java.lang.reflect.Type;
    025    import java.util.Arrays;
    026    import java.util.Iterator;
    027
    028    import cascading.tuple.coerce.Coercions;
    029    import cascading.tuple.type.CoercibleType;
    030    import cascading.util.ForeverValueIterator;
    031    import org.slf4j.Logger;
    032    import org.slf4j.LoggerFactory;
    033
    034    /**
    035     * Class TupleEntry allows a {@link Tuple} instance and its declaring {@link Fields} instance to be used as a single object.
    036     * <p/>
    037     * Once a TupleEntry is created, its Fields cannot be changed, but the Tuple instance it holds can be replaced or
    038     * modified. The managed Tuple should not have elements added or removed, as this will break the relationship with
    039     * the associated Fields instance.
    040     * <p/>
    041     * If type information is provided on the Fields instance, all setters on this class will use that information to
    042     * coerce the given object to the expected type.
    043     * <p/>
    044     * For example, if position is is of type {@code long}, then {@code entry.setString(0, "9" )} will coerce the "9" to a
    045     * long {@code 9}. Thus, {@code entry.getObject(0) == 9l}.
    046     * <p/>
    047     * No coercion is performed with the {@link #getObject(Comparable)} and {@link #getObject(int)} methods.
    048     * <p/>
    049     * To set a value without coercion, see the {@link #setRaw(Comparable, Object)} and {@link #setRaw(int, Object)}
    050     * methods.
    051     *
    052     * @see Fields
    053     * @see Tuple
    054     */
    055    public class TupleEntry
    056      {
    057      private static final Logger LOG = LoggerFactory.getLogger( TupleEntry.class );
    058
    059      private static final CoercibleType[] EMPTY_COERCIONS = new CoercibleType[ 0 ];
    060      private static final ForeverValueIterator<CoercibleType> OBJECT_ITERATOR = new ForeverValueIterator<CoercibleType>( Coercions.OBJECT );
    061
    062      /** An EMPTY TupleEntry instance for use as a stand in instead of a {@code null}. */
    063      public static final TupleEntry NULL = new TupleEntry( Fields.NONE, Tuple.NULL );
    064
    065      /** Field fields */
    066      private Fields fields;
    067
    068      private CoercibleType[] coercions = EMPTY_COERCIONS;
    069
    070      /** Field isUnmodifiable */
    071      private boolean isUnmodifiable = false;
    072      /** Field tuple */
    073      Tuple tuple;
    074
    075      /**
    076       * Method select will select a new Tuple instance from the given set of entries. Entries order is significant to
    077       * the selector.
    078       *
    079       * @param selector of type Fields
    080       * @param entries  of type TupleEntry
    081       * @return Tuple
    082       */
    083      public static Tuple select( Fields selector, TupleEntry... entries )
    084        {
    085        // todo: consider just appending tuples values and just peeking those values
    086        Tuple result = null;
    087
    088        // does not do field checks
    089        if( selector.isAll() )
    090          {
    091          for( TupleEntry entry : entries )
    092            {
    093            if( result == null )
    094              result = entry.getTuple();
    095            else
    096              result = result.append( entry.getTuple() );
    097            }
    098
    099          return result;
    100          }
    101
    102        int size = 0;
    103
    104        for( TupleEntry entry : entries )
    105          size += entry.size();
    106
    107        result = Tuple.size( selector.size() );
    108
    109        int offset = 0;
    110
    111        for( TupleEntry entry : entries )
    112          {
    113          for( int i = 0; i < selector.size(); i++ )
    114            {
    115            Comparable field = selector.get( i );
    116
    117            int pos;
    118
    119            if( field instanceof String )
    120              {
    121              pos = entry.fields.indexOfSafe( field );
    122
    123              if( pos == -1 )
    124                continue;
    125              }
    126            else
    127              {
    128              pos = entry.fields.translatePos( (Integer) field, size ) - offset;
    129
    130              if( pos >= entry.size() || pos < 0 )
    131                continue;
    132              }
    133
    134            result.set( i, entry.getObject( pos ) ); // last in wins
    135            }
    136
    137          offset += entry.size();
    138          }
    139
    140        return result;
    141        }
    142
    143      /** Constructor TupleEntry creates a new TupleEntry instance. */
    144      public TupleEntry()
    145        {
    146        this.fields = Fields.NONE;
    147
    148        setCoercions();
    149        }
    150
    151      /**
    152       * Constructor TupleEntry creates a new TupleEntry instance.
    153       *
    154       * @param isUnmodifiable of type boolean
    155       */
    156      @ConstructorProperties({"isUnmodifiable"})
    157      public TupleEntry( boolean isUnmodifiable )
    158        {
    159        this.fields = Fields.NONE;
    160        this.isUnmodifiable = isUnmodifiable;
    161
    162        setCoercions();
    163        }
    164
    165      /**
    166       * Constructor TupleEntry creates a new TupleEntry instance.
    167       *
    168       * @param fields of type Fields
    169       */
    170      @ConstructorProperties({"fields"})
    171      public TupleEntry( Fields fields )
    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        this.fields = fields;
    188        this.isUnmodifiable = isUnmodifiable;
    189
    190        setCoercions();
    191        }
    192
    193      /**
    194       * Constructor TupleEntry creates a new TupleEntry instance.
    195       *
    196       * @param fields         of type Fields
    197       * @param tuple          of type Tuple
    198       * @param isUnmodifiable of type boolean
    199       */
    200      @ConstructorProperties({"fields", "tuple", "isUnmodifiable"})
    201      public TupleEntry( Fields fields, Tuple tuple, boolean isUnmodifiable )
    202        {
    203        this.fields = fields;
    204        this.isUnmodifiable = isUnmodifiable;
    205        setTuple( tuple );
    206
    207        setCoercions();
    208        }
    209
    210      /**
    211       * Constructor TupleEntry creates a new TupleEntry instance.
    212       *
    213       * @param fields of type Fields
    214       * @param tuple  of type Tuple
    215       */
    216      @ConstructorProperties({"fields", "tuple"})
    217      public TupleEntry( Fields fields, Tuple tuple )
    218        {
    219        this.fields = fields;
    220        this.tuple = tuple;
    221
    222        setCoercions();
    223        }
    224
    225      /**
    226       * Constructor TupleEntry creates a new TupleEntry instance that is a safe copy of the given tupleEntry.
    227       * <p/>
    228       * The new instance is safe to cache and will be modifiable regardless of the given tupleEntry state.
    229       *
    230       * @param tupleEntry of type TupleEntry
    231       */
    232      @ConstructorProperties({"tupleEntry"})
    233      public TupleEntry( TupleEntry tupleEntry )
    234        {
    235        this.fields = tupleEntry.getFields();
    236        this.tuple = tupleEntry.getTupleCopy();
    237
    238        setCoercions();
    239        }
    240
    241      /**
    242       * Constructor TupleEntry creates a new TupleEntry instance.
    243       *
    244       * @param tuple of type Tuple
    245       */
    246      @ConstructorProperties({"tuple"})
    247      public TupleEntry( Tuple tuple )
    248        {
    249        this.fields = Fields.size( tuple.size() );
    250        this.tuple = tuple;
    251
    252        setCoercions();
    253        }
    254
    255      private void setCoercions()
    256        {
    257        if( coercions != EMPTY_COERCIONS )
    258          return;
    259
    260        Fields fields = getFields();
    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( coercions.length < size )
    267          coercions = Coercions.coercibleArray( size, types );
    268        }
    269
    270      /**
    271       * Method isUnmodifiable returns true if this TupleEntry is unmodifiable.
    272       *
    273       * @return boolean
    274       */
    275      public boolean isUnmodifiable()
    276        {
    277        return isUnmodifiable;
    278        }
    279
    280      /**
    281       * Method getFields returns the fields of this TupleEntry object.
    282       *
    283       * @return the fields (type Fields) of this TupleEntry object.
    284       */
    285      public Fields getFields()
    286        {
    287        return fields;
    288        }
    289
    290      /**
    291       * Returns true if there are types associated with this instance.
    292       *
    293       * @return boolean
    294       */
    295      public boolean hasTypes()
    296        {
    297        return fields.hasTypes();
    298        }
    299
    300      /**
    301       * Method getTuple returns the tuple of this TupleEntry object.
    302       *
    303       * @return the tuple (type Tuple) of this TupleEntry object.
    304       */
    305      public Tuple getTuple()
    306        {
    307        return tuple;
    308        }
    309
    310      /**
    311       * Method getTupleCopy returns a copy of the tuple of this TupleEntry object.
    312       *
    313       * @return a copy of the tuple (type Tuple) of this TupleEntry object.
    314       */
    315      public Tuple getTupleCopy()
    316        {
    317        return new Tuple( tuple );
    318        }
    319
    320      /**
    321       * Method getCoercedTuple is a helper method for copying the current tuple elements into a new Tuple,
    322       * of the same size, as the requested coerced types.
    323       *
    324       * @param types of type Type[]
    325       * @return returns the a new Tuple instance with coerced values
    326       */
    327      public Tuple getCoercedTuple( Type[] types )
    328        {
    329        return getCoercedTuple( types, Tuple.size( types.length ) );
    330        }
    331
    332      /**
    333       * Method getCoercedTuple is a helper method for copying the current tuple elements into the new Tuple,
    334       * of the same size, as the requested coerced types.
    335       *
    336       * @param types of type Type[]
    337       * @param into  of type Tuple
    338       * @return returns the given into Tuple instance with coerced values
    339       */
    340      public Tuple getCoercedTuple( Type[] types, Tuple into )
    341        {
    342        if( coercions.length != types.length || types.length != into.size() )
    343          throw new IllegalArgumentException( "current entry and given tuple and types must be same length" );
    344
    345        for( int i = 0; i < coercions.length; i++ )
    346          {
    347          Object element = tuple.getObject( i );
    348          into.set( i, Coercions.coerce( coercions[ i ], element, types[ i ] ) );
    349          }
    350
    351        return into;
    352        }
    353
    354      /**
    355       * Method setTuple sets the tuple of this TupleEntry object, no copy will be performed.
    356       * <p/>
    357       * If the given tuple is "unmodifiable" ({@code Tuple.isUnmodifiable() == true}) and this TupleEntry is
    358       * not "unmodifiable", an exception will be thrown.
    359       * <p/>
    360       * Unmodifiable tuples are generally owned by the system and cannot be be changed and must not be cached.
    361       *
    362       * @param tuple the tuple of this TupleEntry object.
    363       */
    364      public void setTuple( Tuple tuple )
    365        {
    366        if( !isUnmodifiable && tuple.isUnmodifiable() )
    367          throw new IllegalArgumentException( "current entry is modifiable but given tuple is not modifiable, make copy of given Tuple first" );
    368
    369        if( tuple != null && isUnmodifiable )
    370          this.tuple = Tuples.asUnmodifiable( tuple );
    371        else
    372          this.tuple = tuple;
    373
    374        setCoercions();
    375        }
    376
    377      /**
    378       * Method setCanonicalTuple replaces each value of the current tuple with the given tuple elements after
    379       * they are coerced.
    380       * <p/>
    381       * This method will modify the existing Tuple wrapped by this TupleEntry instance even
    382       * if it is marked as unmodifiable.
    383       *
    384       * @param tuple to replace the current wrapped Tuple instance
    385       */
    386      public void setCanonicalTuple( Tuple tuple )
    387        {
    388        if( isUnmodifiable )
    389          tuple = Tuples.asUnmodifiable( tuple );
    390
    391        if( fields.size() != tuple.size() )
    392          throw new IllegalArgumentException( "current entry and given tuple must be same length" );
    393
    394        for( int i = 0; i < coercions.length; i++ )
    395          {
    396          Object element = tuple.getObject( i );
    397
    398          this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
    399          }
    400        }
    401
    402      /**
    403       * Method setCanonicalValues replaces each value of the current tuple with th give Object[]
    404       * after they are coerced.
    405       *
    406       * @param values to replace the current wrapped tuple instance values
    407       */
    408      public void setCanonicalValues( Object[] values )
    409        {
    410        if( fields.size() != values.length )
    411          throw new IllegalArgumentException( "current entry and given array must be same length" );
    412
    413        for( int i = 0; i < coercions.length; i++ )
    414          {
    415          Object element = values[ i ];
    416
    417          this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
    418          }
    419        }
    420
    421      /**
    422       * Method size returns the number of values in this instance.
    423       *
    424       * @return int
    425       */
    426      public int size()
    427        {
    428        return tuple.size();
    429        }
    430
    431      /**
    432       * Method get returns the value in the given position pos.
    433       * <p/>
    434       * This method is deprecated, use {@link #getObject(int)} instead.
    435       *
    436       * @param pos position of the element to return.
    437       * @return Comparable
    438       */
    439      @Deprecated
    440      public Comparable get( int pos )
    441        {
    442        return tuple.get( pos );
    443        }
    444
    445      /**
    446       * Method getObject returns the value in the given position pos.
    447       * <p/>
    448       * No coercion is performed if there is an associated coercible type.
    449       *
    450       * @param pos position of the element to return.
    451       * @return Object
    452       */
    453      public Object getObject( int pos )
    454        {
    455        return tuple.getObject( pos );
    456        }
    457
    458      /**
    459       * Method getObject returns the value in the given field or position as the requested type.
    460       * <p/>
    461       * Coercion is performed to the given type.
    462       *
    463       * @param pos position of the element to return.
    464       * @return Object
    465       */
    466      public Object getObject( int pos, Type type )
    467        {
    468        return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
    469        }
    470
    471      /**
    472       * Method get returns the value in the given field or position.
    473       * <br/>
    474       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    475       * be considered.
    476       * <p/>
    477       * This method is deprecated, use {@link #getObject(Comparable)} instead.
    478       *
    479       * @param fieldName field name or position to return
    480       * @return Comparable
    481       */
    482      @Deprecated
    483      public Comparable get( Comparable fieldName )
    484        {
    485        return tuple.get( fields.getPos( asFieldName( fieldName ) ) );
    486        }
    487
    488      /**
    489       * Method getObject returns the value in the given field or position.
    490       * <br/>
    491       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    492       * be considered.
    493       * <p/>
    494       * No coercion is performed if there is an associated coercible type.
    495       *
    496       * @param fieldName field name or position to return
    497       * @return Comparable
    498       */
    499      public Object getObject( Comparable fieldName )
    500        {
    501        int pos = fields.getPos( asFieldName( fieldName ) );
    502        return tuple.getObject( pos );
    503        }
    504
    505      /**
    506       * Method getObject returns the value in the given field or position as the requested type.
    507       * <br/>
    508       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
    509       * be considered.
    510       * <p/>
    511       * Coercion is performed to the given type.
    512       *
    513       * @param fieldName field name or position to return
    514       * @return Comparable
    515       */
    516      public Object getObject( Comparable fieldName, Type type )
    517        {
    518        int pos = fields.getPos( asFieldName( fieldName ) );
    519        return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
    520        }
    521
    522      /**
    523       * Method set sets the value in the given field or position.
    524       * <p/>
    525       * This method is deprecated in favor of {@link #setRaw(Comparable, Object)}
    526       *
    527       * @param fieldName field name or position to set
    528       * @param value     of type Comparable
    529       */
    530      @Deprecated
    531      public void set( Comparable fieldName, Object value )
    532        {
    533        tuple.set( fields.getPos( asFieldName( fieldName ) ), value );
    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 Tuple.NULL;
    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 given types.
    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 new Iterable<T>()
    989        {
    990        @Override
    991        public Iterator<T> iterator()
    992          {
    993          final Iterator<CoercibleType> coercibleIterator = coercions.length == 0 ?
    994            OBJECT_ITERATOR :
    995            Arrays.asList( coercions ).iterator();
    996
    997          final Iterator valuesIterator = tuple.iterator();
    998
    999          return new Iterator<T>()
    1000          {
    1001          @Override
    1002          public boolean hasNext()
    1003            {
    1004            return valuesIterator.hasNext();
    1005            }
    1006
    1007          @Override
    1008          public T next()
    1009            {
    1010            Object next = valuesIterator.next();
    1011
    1012            return (T) coercibleIterator.next().coerce( next, type );
    1013            }
    1014
    1015          @Override
    1016          public void remove()
    1017            {
    1018            valuesIterator.remove();
    1019            }
    1020          };
    1021          }
    1022        };
    1023        }
    1024      }