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