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