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    
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( into == null )
349          throw new IllegalArgumentException( "into argument Tuple may not be null" );
350    
351        if( coercions.length != types.length || types.length != into.size() )
352          throw new IllegalArgumentException( "current entry and given tuple and types must be same length" );
353    
354        for( int i = 0; i < coercions.length; i++ )
355          {
356          Object element = tuple.getObject( i );
357          into.set( i, Coercions.coerce( coercions[ i ], element, types[ i ] ) );
358          }
359    
360        return into;
361        }
362    
363      /**
364       * Method setTuple sets the tuple of this TupleEntry object, no copy will be performed.
365       * <p/>
366       * If the given tuple is "unmodifiable" ({@code Tuple.isUnmodifiable() == true}) and this TupleEntry is
367       * not "unmodifiable", an exception will be thrown.
368       * <p/>
369       * Unmodifiable tuples are generally owned by the system and cannot be be changed and must not be cached.
370       *
371       * @param tuple the tuple of this TupleEntry object.
372       */
373      public void setTuple( Tuple tuple )
374        {
375        if( !isUnmodifiable && tuple != null && tuple.isUnmodifiable() )
376          throw new IllegalArgumentException( "current entry is modifiable but given tuple is not modifiable, make copy of given Tuple first" );
377    
378        if( tuple != null && isUnmodifiable )
379          this.tuple = Tuples.asUnmodifiable( tuple );
380        else
381          this.tuple = tuple;
382    
383        setCoercions();
384        }
385    
386      /**
387       * Method setCanonicalTuple replaces each value of the current tuple with the given tuple elements after
388       * they are coerced.
389       * <p/>
390       * This method will modify the existing Tuple wrapped by this TupleEntry instance even
391       * if it is marked as unmodifiable.
392       * <p/>
393       * If tuple argument is {@code null}, the current tuple will be set to {@code null}.
394       *
395       * @param tuple to replace the current wrapped Tuple instance
396       */
397      public void setCanonicalTuple( Tuple tuple )
398        {
399        if( tuple == null )
400          {
401          this.tuple = null;
402          return;
403          }
404    
405        if( isUnmodifiable )
406          tuple = Tuples.asUnmodifiable( tuple );
407    
408        if( fields.size() != tuple.size() )
409          throw new IllegalArgumentException( "current entry and given tuple must be same length" );
410    
411        for( int i = 0; i < coercions.length; i++ )
412          {
413          Object element = tuple.getObject( i );
414    
415          this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
416          }
417        }
418    
419      /**
420       * Method setCanonicalValues replaces each value of the current tuple with th give Object[]
421       * after they are coerced.
422       *
423       * @param values to replace the current wrapped tuple instance values
424       */
425      public void setCanonicalValues( Object[] values )
426        {
427        if( fields.size() != values.length )
428          throw new IllegalArgumentException( "current entry and given array must be same length" );
429    
430        for( int i = 0; i < coercions.length; i++ )
431          {
432          Object element = values[ i ];
433    
434          this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type
435          }
436        }
437    
438      /**
439       * Method size returns the number of values in this instance.
440       *
441       * @return int
442       */
443      public int size()
444        {
445        return tuple.size();
446        }
447    
448      /**
449       * Method get returns the value in the given position pos.
450       * <p/>
451       * This method is deprecated, use {@link #getObject(int)} instead.
452       *
453       * @param pos position of the element to return.
454       * @return Comparable
455       */
456      @Deprecated
457      public Comparable get( int pos )
458        {
459        return tuple.get( pos );
460        }
461    
462      /**
463       * Method getObject returns the value in the given position pos.
464       * <p/>
465       * No coercion is performed if there is an associated coercible type.
466       *
467       * @param pos position of the element to return.
468       * @return Object
469       */
470      public Object getObject( int pos )
471        {
472        return tuple.getObject( pos );
473        }
474    
475      /**
476       * Method getObject returns the value in the given field or position as the requested type.
477       * <p/>
478       * Coercion is performed to the given type.
479       *
480       * @param pos position of the element to return.
481       * @return Object
482       */
483      public Object getObject( int pos, Type type )
484        {
485        return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
486        }
487    
488      /**
489       * Method get returns the value in the given field or position.
490       * <br/>
491       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
492       * be considered.
493       * <p/>
494       * This method is deprecated, use {@link #getObject(Comparable)} instead.
495       *
496       * @param fieldName field name or position to return
497       * @return Comparable
498       */
499      @Deprecated
500      public Comparable get( Comparable fieldName )
501        {
502        return tuple.get( fields.getPos( asFieldName( fieldName ) ) );
503        }
504    
505      /**
506       * Method getObject returns the value in the given field or position.
507       * <br/>
508       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
509       * be considered.
510       * <p/>
511       * No coercion is performed if there is an associated coercible type.
512       *
513       * @param fieldName field name or position to return
514       * @return Comparable
515       */
516      public Object getObject( Comparable fieldName )
517        {
518        int pos = fields.getPos( asFieldName( fieldName ) );
519        return tuple.getObject( pos );
520        }
521    
522      /**
523       * Method getObject returns the value in the given field or position as the requested type.
524       * <br/>
525       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
526       * be considered.
527       * <p/>
528       * Coercion is performed to the given type.
529       *
530       * @param fieldName field name or position to return
531       * @return Comparable
532       */
533      public Object getObject( Comparable fieldName, Type type )
534        {
535        int pos = fields.getPos( asFieldName( fieldName ) );
536        return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type );
537        }
538    
539      /**
540       * Method set sets the value in the given field or position.
541       * <p/>
542       * This method is deprecated in favor of {@link #setRaw(Comparable, Object)}
543       *
544       * @param fieldName field name or position to set
545       * @param value     of type Comparable
546       */
547      @Deprecated
548      public void set( Comparable fieldName, Object value )
549        {
550        tuple.set( fields.getPos( asFieldName( fieldName ) ), value );
551        }
552    
553      /**
554       * Method set sets the value in the given position.
555       * <p/>
556       * No coercion is performed if there is an associated coercible type.
557       *
558       * @param pos   position to set
559       * @param value of type Comparable
560       */
561      public void setRaw( int pos, Object value )
562        {
563        tuple.set( pos, value );
564        }
565    
566      /**
567       * Method set sets the value in the given field or position.
568       * <p/>
569       * No coercion is performed if there is an associated coercible type.
570       *
571       * @param fieldName field name or position to set
572       * @param value     of type Comparable
573       */
574      public void setRaw( Comparable fieldName, Object value )
575        {
576        tuple.set( fields.getPos( asFieldName( fieldName ) ), value );
577        }
578    
579      /**
580       * Method set sets the value in the given field or position.
581       *
582       * @param fieldName field name or position to set
583       * @param value     of type Comparable
584       */
585      public void setObject( Comparable fieldName, Object value )
586        {
587        int pos = fields.getPos( asFieldName( fieldName ) );
588    
589        tuple.set( pos, coercions[ pos ].canonical( value ) );
590        }
591    
592      /**
593       * Method setBoolean sets the value in the given field or position.
594       *
595       * @param fieldName field name or position to set
596       * @param value     of type boolean
597       */
598      public void setBoolean( Comparable fieldName, boolean value )
599        {
600        int pos = fields.getPos( asFieldName( fieldName ) );
601    
602        tuple.set( pos, coercions[ pos ].canonical( value ) );
603        }
604    
605      /**
606       * Method setShort sets the value in the given field or position.
607       *
608       * @param fieldName field name or position to set
609       * @param value     of type short
610       */
611      public void setShort( Comparable fieldName, short value )
612        {
613        int pos = fields.getPos( asFieldName( fieldName ) );
614    
615        tuple.set( pos, coercions[ pos ].canonical( value ) );
616        }
617    
618      /**
619       * Method setInteger sets the value in the given field or position.
620       *
621       * @param fieldName field name or position to set
622       * @param value     of type int
623       */
624      public void setInteger( Comparable fieldName, int value )
625        {
626        int pos = fields.getPos( asFieldName( fieldName ) );
627    
628        tuple.set( pos, coercions[ pos ].canonical( value ) );
629        }
630    
631      /**
632       * Method setLong sets the value in the given field or position.
633       *
634       * @param fieldName field name or position to set
635       * @param value     of type long
636       */
637      public void setLong( Comparable fieldName, long value )
638        {
639        int pos = fields.getPos( asFieldName( fieldName ) );
640    
641        tuple.set( pos, coercions[ pos ].canonical( value ) );
642        }
643    
644      /**
645       * Method setFloat sets the value in the given field or position.
646       *
647       * @param fieldName field name or position to set
648       * @param value     of type float
649       */
650      public void setFloat( Comparable fieldName, float value )
651        {
652        int pos = fields.getPos( asFieldName( fieldName ) );
653    
654        tuple.set( pos, coercions[ pos ].canonical( value ) );
655        }
656    
657      /**
658       * Method setDouble sets the value in the given field or position.
659       *
660       * @param fieldName field name or position to set
661       * @param value     of type double
662       */
663      public void setDouble( Comparable fieldName, double value )
664        {
665        int pos = fields.getPos( asFieldName( fieldName ) );
666    
667        tuple.set( pos, coercions[ pos ].canonical( value ) );
668        }
669    
670      /**
671       * Method setString sets the value in the given field or position.
672       *
673       * @param fieldName field name or position to set
674       * @param value     of type String
675       */
676      public void setString( Comparable fieldName, String value )
677        {
678        int pos = fields.getPos( asFieldName( fieldName ) );
679        tuple.set( pos, coercions[ pos ].canonical( value ) );
680        }
681    
682      /**
683       * Method getString returns the element for the given field name or position as a String.
684       * <br/>
685       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
686       * be considered.
687       *
688       * @param fieldName field name or position to return
689       * @return String
690       */
691      public String getString( Comparable fieldName )
692        {
693        return (String) getObject( fieldName, String.class );
694        }
695    
696      /**
697       * Method getFloat returns the element for the given field name or position as a float. Zero if null.
698       * <br/>
699       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
700       * be considered.
701       *
702       * @param fieldName field name or position to return
703       * @return float
704       */
705      public float getFloat( Comparable fieldName )
706        {
707        return (Float) getObject( fieldName, float.class );
708        }
709    
710      /**
711       * Method getDouble returns the element for the given field name or position as a double. Zero if null.
712       * <br/>
713       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
714       * be considered.
715       *
716       * @param fieldName field name or position to return
717       * @return double
718       */
719      public double getDouble( Comparable fieldName )
720        {
721        return (Double) getObject( fieldName, double.class );
722        }
723    
724      /**
725       * Method getInteger  returns the element for the given field name or position as an int. Zero if null.
726       * <br/>
727       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
728       * be considered.
729       *
730       * @param fieldName field name or position to return
731       * @return int
732       */
733      public int getInteger( Comparable fieldName )
734        {
735        return (Integer) getObject( fieldName, int.class );
736        }
737    
738      /**
739       * Method getLong returns the element for the given field name or position as a long. Zero if null.
740       * <br/>
741       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
742       * be considered.
743       *
744       * @param fieldName field name or position to return
745       * @return long
746       */
747      public long getLong( Comparable fieldName )
748        {
749        return (Long) getObject( fieldName, long.class );
750        }
751    
752      /**
753       * Method getShort returns the element for the given field name or position as a short. Zero if null.
754       * <br/>
755       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
756       * be considered.
757       *
758       * @param fieldName field name or position to return
759       * @return short
760       */
761      public short getShort( Comparable fieldName )
762        {
763        return (Short) getObject( fieldName, short.class );
764        }
765    
766      /**
767       * Method getBoolean returns the element for the given field name or position as a boolean.
768       * If the value is (case ignored) the string 'true', a {@code true} value will be returned. {@code false} if null.
769       * <br/>
770       * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will
771       * be considered.
772       *
773       * @param fieldName field name or position to return
774       * @return boolean
775       */
776      public boolean getBoolean( Comparable fieldName )
777        {
778        return (Boolean) getObject( fieldName, boolean.class );
779        }
780    
781      private Comparable asFieldName( Comparable fieldName )
782        {
783        return Fields.asFieldName( fieldName );
784        }
785    
786      /**
787       * Method selectEntry selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the
788       * same fields as declared are given, {@code this} will be returned.
789       * <p/>
790       * The returned TupleEntry will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance.
791       * <p/>
792       * See {@link #selectEntryCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection.
793       * <p/>
794       * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent
795       * on the given selector.
796       *
797       * @param selector Fields selector that selects the values to return
798       * @return TupleEntry
799       */
800      public TupleEntry selectEntry( Fields selector )
801        {
802        if( selector == null || selector.isAll() || fields == selector ) // == is intentional
803          return this;
804    
805        if( selector.isNone() )
806          return isUnmodifiable ? TupleEntry.NULL : new TupleEntry();
807    
808        return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ), isUnmodifiable );
809        }
810    
811      /**
812       * Method selectEntry selects the fields specified in selector from this instance.
813       * <p/>
814       * It is guaranteed to return a new modifiable TupleEntry instance at a cost of copying data.
815       * <p/>
816       * The returned instance is safe to cache.
817       *
818       * @param selector Fields selector that selects the values to return
819       * @return TupleEntry
820       */
821      public TupleEntry selectEntryCopy( Fields selector )
822        {
823        if( selector == null || selector.isAll() || fields == selector ) // == is intentional
824          return new TupleEntry( this );
825    
826        if( selector.isNone() )
827          return new TupleEntry();
828    
829        return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ) );
830        }
831    
832      /**
833       * Method selectTuple selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the
834       * same fields as declared are given, {@code this.getTuple()} will be returned.
835       * <p/>
836       * The returned Tuple will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance.
837       * <p/>
838       * See {@link #selectTupleCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection.
839       * <p/>
840       * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent
841       * on the given selector.
842       *
843       * @param selector Fields selector that selects the values to return
844       * @return Tuple
845       */
846      public Tuple selectTuple( Fields selector )
847        {
848        if( selector == null || selector.isAll() || fields == selector ) // == is intentional
849          return this.tuple;
850    
851        if( selector.isNone() )
852          return Tuple.NULL;
853    
854        Tuple result = tuple.get( fields, selector );
855    
856        if( isUnmodifiable )
857          Tuples.asUnmodifiable( result );
858    
859        return result;
860        }
861    
862      /**
863       * Method selectTupleCopy selects the fields specified in selector from this instance.
864       * <p/>
865       * It is guaranteed to return a new modifiable Tuple instance at a cost of copying data.
866       * <p/>
867       * The returned instance is safe to cache.
868       *
869       * @param selector Fields selector that selects the values to return
870       * @return Tuple
871       */
872      public Tuple selectTupleCopy( Fields selector )
873        {
874        if( selector == null || selector.isAll() || fields == selector ) // == is intentional
875          return new Tuple( this.tuple );
876    
877        if( selector.isNone() )
878          return new Tuple();
879    
880        return tuple.get( fields, selector );
881        }
882    
883      /**
884       * Method selectInto selects the fields specified in the selector from this instance and copies
885       * them into the given tuple argument.
886       *
887       * @param selector of type Fields
888       * @param tuple    of type Tuple
889       * @return returns the given tuple argument with new values added
890       */
891      public Tuple selectInto( Fields selector, Tuple tuple )
892        {
893        if( selector.isNone() )
894          return tuple;
895    
896        int[] pos = this.tuple.getPos( fields, selector );
897    
898        if( pos == null || pos.length == 0 )
899          {
900          tuple.addAll( this.tuple );
901          }
902        else
903          {
904          for( int i : pos )
905            tuple.add( this.tuple.getObject( i ) );
906          }
907    
908        return tuple;
909        }
910    
911      /**
912       * Method setTuple sets the values specified by the selector to the values given by the given tuple, the given
913       * values will always be copied into this TupleEntry.
914       *
915       * @param selector of type Fields
916       * @param tuple    of type Tuple
917       */
918      public void setTuple( Fields selector, Tuple tuple )
919        {
920        if( selector == null || selector.isAll() )
921          this.tuple.setAll( tuple );
922        else
923          this.tuple.set( fields, selector, tuple );
924        }
925    
926      /**
927       * Method set sets the values from the given tupleEntry into this TupleEntry instance based on the given
928       * tupleEntry field names.
929       * <p/>
930       * If type information is given, each incoming value will be coerced from its canonical type to the current type.
931       *
932       * @param tupleEntry of type TupleEntry
933       */
934      public void set( TupleEntry tupleEntry )
935        {
936        this.tuple.set( fields, tupleEntry.getFields(), tupleEntry.getTuple(), tupleEntry.coercions );
937        }
938    
939      /**
940       * Method appendNew appends the given TupleEntry instance to this instance.
941       *
942       * @param entry of type TupleEntry
943       * @return TupleEntry
944       */
945      public TupleEntry appendNew( TupleEntry entry )
946        {
947        Fields appendedFields = fields.append( entry.fields.isUnknown() ? Fields.size( entry.tuple.size() ) : entry.fields );
948        Tuple appendedTuple = tuple.append( entry.tuple );
949    
950        return new TupleEntry( appendedFields, appendedTuple );
951        }
952    
953      @Override
954      public boolean equals( Object object )
955        {
956        if( this == object )
957          return true;
958    
959        if( !( object instanceof TupleEntry ) )
960          return false;
961    
962        TupleEntry that = (TupleEntry) object;
963    
964        if( fields != null ? !fields.equals( that.fields ) : that.fields != null )
965          return false;
966    
967        // use comparators if in the this side fields instance
968        if( tuple != null ? fields.compare( tuple, that.tuple ) != 0 : that.tuple != null )
969          return false;
970    
971        return true;
972        }
973    
974      @Override
975      public int hashCode()
976        {
977        int result = fields != null ? fields.hashCode() : 0;
978        result = 31 * result + ( tuple != null ? tuple.hashCode() : 0 );
979        return result;
980        }
981    
982      @Override
983      public String toString()
984        {
985        if( fields == null )
986          return "empty";
987        else if( tuple == null )
988          return "fields: " + fields.print();
989        else
990          return "fields: " + fields.print() + " tuple: " + tuple.print();
991        }
992    
993      /**
994       * Method asIterableOf returns an {@link Iterable} instance that will coerce all Tuple elements
995       * into the given {@code type} parameter.
996       * <p/>
997       * This method honors any {@link cascading.tuple.type.CoercibleType} instances on the internal
998       * Fields instance for the specified Tuple element.
999       *
1000       * @param type of type Class
1001       * @return an Iterable
1002       */
1003      public <T> Iterable<T> asIterableOf( final Class<T> type )
1004        {
1005        return new Iterable<T>()
1006        {
1007        @Override
1008        public Iterator<T> iterator()
1009          {
1010          final Iterator<CoercibleType> coercibleIterator = coercions.length == 0 ?
1011            OBJECT_ITERATOR :
1012            Arrays.asList( coercions ).iterator();
1013    
1014          final Iterator valuesIterator = tuple.iterator();
1015    
1016          return new Iterator<T>()
1017          {
1018          @Override
1019          public boolean hasNext()
1020            {
1021            return valuesIterator.hasNext();
1022            }
1023    
1024          @Override
1025          public T next()
1026            {
1027            Object next = valuesIterator.next();
1028    
1029            return (T) coercibleIterator.next().coerce( next, type );
1030            }
1031    
1032          @Override
1033          public void remove()
1034            {
1035            valuesIterator.remove();
1036            }
1037          };
1038          }
1039        };
1040        }
1041      }