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