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    {
990    final Iterator<CoercibleType> coercibleIterator = coercions.length == 0 ? OBJECT_ITERATOR : Arrays.asList( coercions ).iterator();
991    final Iterator valuesIterator = tuple.iterator();
992
993    return new Iterator<T>()
994      {
995      @Override
996      public boolean hasNext()
997        {
998        return valuesIterator.hasNext();
999        }
1000
1001      @Override
1002      public T next()
1003        {
1004        Object next = valuesIterator.next();
1005
1006        return (T) coercibleIterator.next().coerce( next, type );
1007        }
1008
1009      @Override
1010      public void remove()
1011        {
1012        valuesIterator.remove();
1013        }
1014      };
1015    };
1016    }
1017  }