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