001/*
002 * Copyright (c) 2007-2016 Concurrent, Inc. All Rights Reserved.
003 *
004 * Project and contact information: http://www.cascading.org/
005 *
006 * This file is part of the Cascading project.
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020
021package cascading.tuple;
022
023import java.beans.ConstructorProperties;
024import java.io.Serializable;
025import java.lang.reflect.Type;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.Comparator;
029import java.util.Formatter;
030import java.util.Iterator;
031import java.util.List;
032
033import cascading.tuple.coerce.Coercions;
034import cascading.tuple.type.CoercibleType;
035import cascading.util.Util;
036
037/**
038 * A Tuple represents a set of values. Consider a Tuple the same as a database record where every value is a column in
039 * that table.
040 * <p/>
041 * A "tuple stream" is a set of Tuple instances passed consecutively through a Pipe assembly.
042 * <p/>
043 * Tuples work in tandem with {@link Fields} and the {@link TupleEntry} classes. A TupleEntry holds an instance of
044 * Fields and a Tuple. It allows a tuple to be accessed by its field names, and will help maintain consistent types
045 * if any are given on the Fields instance. That is, if a field is specified at an Integer, calling {@link #set(int, Object)}
046 * with a String will force the String to be coerced into a Integer instance.
047 * <p/>
048 * For managing custom types, see the {@link CoercibleType} interface which extends {@link Type}.
049 * <p/>
050 * Tuple instances created by user code, by default, are mutable (or modifiable).
051 * Tuple instances created by the system are immutable (or unmodifiable, tested by calling {@link #isUnmodifiable()}).
052 * <p/>
053 * For example tuples returned by
054 * {@link cascading.operation.FunctionCall#getArguments()}, will always be unmodifiable. Thus they must be copied
055 * if they will be changed by user code or cached in the local context. See the Tuple copy constructor, or {@code *Copy()} methods
056 * on {@link TupleEntry}.
057 * <p/>
058 * Because a Tuple can hold any Object type, it is suitable for storing custom types. But all custom types
059 * must have a serialization support per the underlying framework.
060 * <p/>
061 * For Hadoop, a {@link org.apache.hadoop.io.serializer.Serialization} implementation
062 * must be registered with Hadoop. For further performance improvements, see the
063 * {@link cascading.tuple.hadoop.SerializationToken} Java annotation.
064 *
065 * @see org.apache.hadoop.io.serializer.Serialization
066 * @see cascading.tuple.hadoop.SerializationToken
067 */
068public class Tuple implements Comparable<Object>, Iterable<Object>, Serializable
069  {
070  /** A constant empty Tuple instance. This instance is immutable. */
071  public static final Tuple NULL = Tuples.asUnmodifiable( new Tuple() );
072
073  /** Field printDelim */
074  private final static String printDelim = "\t";
075
076  /** Field isUnmodifiable */
077  protected transient boolean isUnmodifiable = false;
078
079  /** Field elements */
080  protected List<Object> elements;
081
082  /**
083   * Method size returns a new Tuple instance of the given size with nulls as its element values.
084   *
085   * @param size of type int
086   * @return Tuple
087   */
088  public static Tuple size( int size )
089    {
090    return size( size, null );
091    }
092
093  /**
094   * Method size returns a new Tuple instance of the given size with the given Comparable as its element values.
095   *
096   * @param size  of type int
097   * @param value of type Comparable
098   * @return Tuple
099   */
100  public static Tuple size( int size, Comparable value )
101    {
102    Tuple result = new Tuple( new ArrayList<Object>( size ) );
103
104    for( int i = 0; i < size; i++ )
105      result.add( value );
106
107    return result;
108    }
109
110  /**
111   * Returns a reference to the private elements of the given Tuple.
112   * <p/>
113   * This method is for internal use and is subject to change scope in a future release.
114   *
115   * @param tuple of type Tuple
116   * @return List<Comparable>
117   */
118  public static List<Object> elements( Tuple tuple )
119    {
120    return tuple.elements;
121    }
122
123  protected Tuple( List<Object> elements )
124    {
125    this.elements = elements;
126    }
127
128  /** Constructor Tuple creates a new Tuple instance. */
129  public Tuple()
130    {
131    this( new ArrayList<Object>() );
132    }
133
134  /**
135   * Copy constructor. Does not nest the given Tuple instance within this new instance. Use {@link #add(Object)}.
136   *
137   * @param tuple of type Tuple
138   */
139  @ConstructorProperties({"tuple"})
140  public Tuple( Tuple tuple )
141    {
142    this( new ArrayList<Object>( tuple.elements ) );
143    }
144
145  /**
146   * Constructor Tuple creates a new Tuple instance with all the given values.
147   *
148   * @param values of type Object...
149   */
150  @ConstructorProperties({"values"})
151  public Tuple( Object... values )
152    {
153    this( new ArrayList<Object>( values.length ) );
154    Collections.addAll( elements, values );
155    }
156
157  /**
158   * Method isUnmodifiable returns true if this Tuple instance is unmodifiable.
159   * <p/>
160   * "Unmodifiable" tuples are generally owned by the system and cannot be changed, nor should they be cached
161   * as the internal values may change.
162   *
163   * @return boolean
164   */
165  public boolean isUnmodifiable()
166    {
167    return isUnmodifiable;
168    }
169
170  /**
171   * Method get returns the element at the given position.
172   * <p/>
173   * This method will perform no coercion on the element.
174   *
175   * @param pos of type int
176   * @return Object
177   */
178  public Object getObject( int pos )
179    {
180    return elements.get( pos );
181    }
182
183  /**
184   * Method getChar returns the element at the given position as a char.
185   *
186   * @param pos of type int
187   * @return String
188   */
189  public char getChar( int pos )
190    {
191    return Coercions.CHARACTER.coerce( getObject( pos ) );
192    }
193
194  /**
195   * Method getString returns the element at the given position as a String.
196   *
197   * @param pos of type int
198   * @return String
199   */
200  public String getString( int pos )
201    {
202    return Coercions.STRING.coerce( getObject( pos ) );
203    }
204
205  /**
206   * Method getFloat returns the element at the given position as a float. Zero if null.
207   *
208   * @param pos of type int
209   * @return float
210   */
211  public float getFloat( int pos )
212    {
213    return Coercions.FLOAT.coerce( getObject( pos ) );
214    }
215
216  /**
217   * Method getDouble returns the element at the given position as a double. Zero if null.
218   *
219   * @param pos of type int
220   * @return double
221   */
222  public double getDouble( int pos )
223    {
224    return Coercions.DOUBLE.coerce( getObject( pos ) );
225    }
226
227  /**
228   * Method getInteger returns the element at the given position as an int. Zero if null.
229   *
230   * @param pos of type int
231   * @return int
232   */
233  public int getInteger( int pos )
234    {
235    return Coercions.INTEGER.coerce( getObject( pos ) );
236    }
237
238  /**
239   * Method getLong returns the element at the given position as an long. Zero if null.
240   *
241   * @param pos of type int
242   * @return long
243   */
244  public long getLong( int pos )
245    {
246    return Coercions.LONG.coerce( getObject( pos ) );
247    }
248
249  /**
250   * Method getShort returns the element at the given position as an short. Zero if null.
251   *
252   * @param pos of type int
253   * @return long
254   */
255  public short getShort( int pos )
256    {
257    return Coercions.SHORT.coerce( getObject( pos ) );
258    }
259
260  /**
261   * Method getBoolean returns the element at the given position as a boolean. If the value is (case ignored) the
262   * string 'true', a {@code true} value will be returned. {@code false} if null.
263   *
264   * @param pos of type int
265   * @return boolean
266   */
267  public boolean getBoolean( int pos )
268    {
269    return Coercions.BOOLEAN.coerce( getObject( pos ) );
270    }
271
272  /**
273   * Method get will return a new Tuple instance populated with element values from the given array of positions.
274   *
275   * @param pos of type int[]
276   * @return Tuple
277   */
278  public Tuple get( int[] pos )
279    {
280    if( pos == null || pos.length == 0 )
281      return new Tuple( this );
282
283    Tuple results = new Tuple();
284
285    for( int i : pos )
286      results.add( elements.get( i ) );
287
288    return results;
289    }
290
291  /**
292   * Method get returns a new Tuple populated with only those values whose field names are specified in the given
293   * selector. The declarator Fields instance declares the fields and positions in the current Tuple instance.
294   *
295   * @param declarator of type Fields
296   * @param selector   of type Fields
297   * @return Tuple
298   */
299  public Tuple get( Fields declarator, Fields selector )
300    {
301    try
302      {
303      return get( getPos( declarator, selector ) );
304      }
305    catch( Exception exception )
306      {
307      throw new TupleException( "unable to select from: " + declarator.print() + ", using selector: " + selector.print(), exception );
308      }
309    }
310
311  public int[] getPos( Fields declarator, Fields selector )
312    {
313    if( !declarator.isUnknown() && elements.size() != declarator.size() )
314      throw new TupleException( "field declaration: " + declarator.print() + ", does not match tuple: " + print() );
315
316    return declarator.getPos( selector, size() );
317    }
318
319  /**
320   * Method is the inverse of {@link #remove(int[])}.
321   *
322   * @param pos of type int[]
323   * @return Tuple
324   */
325  public Tuple leave( int[] pos )
326    {
327    verifyModifiable();
328
329    Tuple results = remove( pos );
330
331    List<Object> temp = results.elements;
332    results.elements = this.elements;
333    this.elements = temp;
334
335    return results;
336    }
337
338  /** Method clear empties this Tuple instance. A subsequent call to {@link #size()} will return zero ({@code 0}). */
339  public void clear()
340    {
341    verifyModifiable();
342
343    elements.clear();
344    }
345
346  /**
347   * Method add adds a new element value to this instance.
348   *
349   * @param value of type Comparable
350   */
351  public void add( Comparable value )
352    {
353    add( (Object) value );
354    }
355
356  /**
357   * Method add adds a new element value to this instance.
358   *
359   * @param value of type Object
360   */
361  public void add( Object value )
362    {
363    verifyModifiable();
364
365    elements.add( value );
366    }
367
368  /**
369   * Method addBoolean adds a new element value to this instance.
370   *
371   * @param value of type boolean
372   */
373  public void addBoolean( boolean value )
374    {
375    verifyModifiable();
376
377    elements.add( value );
378    }
379
380  /**
381   * Method addShort adds a new element value to this instance.
382   *
383   * @param value of type short
384   */
385  public void addShort( short value )
386    {
387    verifyModifiable();
388
389    elements.add( value );
390    }
391
392  /**
393   * Method addInteger adds a new element value to this instance.
394   *
395   * @param value of type int
396   */
397  public void addInteger( int value )
398    {
399    verifyModifiable();
400
401    elements.add( value );
402    }
403
404  /**
405   * Method addLong adds a new element value to this instance.
406   *
407   * @param value of type long
408   */
409  public void addLong( long value )
410    {
411    verifyModifiable();
412
413    elements.add( value );
414    }
415
416  /**
417   * Method addFloat adds a new element value to this instance.
418   *
419   * @param value of type float
420   */
421  public void addFloat( float value )
422    {
423    verifyModifiable();
424
425    elements.add( value );
426    }
427
428  /**
429   * Method addDouble adds a new element value to this instance.
430   *
431   * @param value of type double
432   */
433  public void addDouble( double value )
434    {
435    verifyModifiable();
436
437    elements.add( value );
438    }
439
440  /**
441   * Method addString adds a new element value to this instance.
442   *
443   * @param value of type String
444   */
445  public void addString( String value )
446    {
447    verifyModifiable();
448
449    elements.add( value );
450    }
451
452  /**
453   * Method addAll adds all given values to this instance.
454   *
455   * @param values of type Object...
456   */
457  public void addAll( Object... values )
458    {
459    verifyModifiable();
460
461    if( values.length == 1 && values[ 0 ] instanceof Tuple )
462      addAll( (Tuple) values[ 0 ] );
463    else
464      Collections.addAll( elements, values );
465    }
466
467  /**
468   * Method addAll adds all the element values of the given Tuple instance to this instance.
469   *
470   * @param tuple of type Tuple
471   */
472  public void addAll( Tuple tuple )
473    {
474    verifyModifiable();
475
476    if( tuple != null )
477      elements.addAll( tuple.elements );
478    }
479
480  /**
481   * Method setAll sets each element value of the given Tuple instance into the corresponding
482   * position of this instance.
483   *
484   * @param tuple of type Tuple
485   */
486  public void setAll( Tuple tuple )
487    {
488    verifyModifiable();
489
490    if( tuple == null )
491      return;
492
493    for( int i = 0; i < tuple.elements.size(); i++ )
494      internalSet( i, tuple.elements.get( i ) );
495    }
496
497  /**
498   * Method setAll sets each element value of the given Tuple instances into the corresponding
499   * position of this instance.
500   * <p/>
501   * All given tuple instances after the first will be offset by the length of the prior tuple instances.
502   *
503   * @param tuples of type Tuple[]
504   */
505  public void setAll( Tuple... tuples )
506    {
507    verifyModifiable();
508
509    if( tuples.length == 0 )
510      return;
511
512    int pos = 0;
513    for( int i = 0; i < tuples.length; i++ )
514      {
515      Tuple tuple = tuples[ i ];
516
517      if( tuple == null ) // being defensive
518        continue;
519
520      for( int j = 0; j < tuple.elements.size(); j++ )
521        internalSet( pos++, tuple.elements.get( j ) );
522      }
523    }
524
525  /**
526   * Method set sets the given value to the given index position in this instance.
527   *
528   * @param index of type int
529   * @param value of type Object
530   */
531  public void set( int index, Object value )
532    {
533    verifyModifiable();
534
535    internalSet( index, value );
536    }
537
538  /**
539   * Method setBoolean sets the given value to the given index position in this instance.
540   *
541   * @param index of type int
542   * @param value of type boolean
543   */
544  public void setBoolean( int index, boolean value )
545    {
546    verifyModifiable();
547
548    internalSet( index, value );
549    }
550
551  /**
552   * Method setShort sets the given value to the given index position in this instance.
553   *
554   * @param index of type int
555   * @param value of type short
556   */
557  public void setShort( int index, short value )
558    {
559    verifyModifiable();
560
561    internalSet( index, value );
562    }
563
564  /**
565   * Method setInteger sets the given value to the given index position in this instance.
566   *
567   * @param index of type int
568   * @param value of type int
569   */
570  public void setInteger( int index, int value )
571    {
572    verifyModifiable();
573
574    internalSet( index, value );
575    }
576
577  /**
578   * Method setLong sets the given value to the given index position in this instance.
579   *
580   * @param index of type int
581   * @param value of type long
582   */
583  public void setLong( int index, long value )
584    {
585    verifyModifiable();
586
587    internalSet( index, value );
588    }
589
590  /**
591   * Method setFloat sets the given value to the given index position in this instance.
592   *
593   * @param index of type int
594   * @param value of type float
595   */
596  public void setFloat( int index, float value )
597    {
598    verifyModifiable();
599
600    internalSet( index, value );
601    }
602
603  /**
604   * Method setDouble sets the given value to the given index position in this instance.
605   *
606   * @param index of type int
607   * @param value of type double
608   */
609  public void setDouble( int index, double value )
610    {
611    verifyModifiable();
612
613    internalSet( index, value );
614    }
615
616  /**
617   * Method setString sets the given value to the given index position in this instance.
618   *
619   * @param index of type int
620   * @param value of type String
621   */
622  public void setString( int index, String value )
623    {
624    verifyModifiable();
625
626    internalSet( index, value );
627    }
628
629  protected final void internalSet( int index, Object value )
630    {
631    try
632      {
633      elements.set( index, value );
634      }
635    catch( IndexOutOfBoundsException exception )
636      {
637      if( elements.size() != 0 )
638        throw new TupleException( "failed to set a value beyond the end of the tuple elements array, size: " + size() + " , index: " + index );
639      else
640        throw new TupleException( "failed to set a value, tuple may not be initialized with values, is zero length" );
641      }
642    }
643
644  /**
645   * Method put places the values of the given tuple into the positions specified by the fields argument. The declarator
646   * Fields value declares the fields in this Tuple instance.
647   *
648   * @param declarator of type Fields
649   * @param fields     of type Fields
650   * @param tuple      of type Tuple
651   */
652  public void put( Fields declarator, Fields fields, Tuple tuple )
653    {
654    verifyModifiable();
655
656    int[] pos = getPos( declarator, fields );
657
658    for( int i = 0; i < pos.length; i++ )
659      internalSet( pos[ i ], tuple.getObject( i ) );
660    }
661
662  /**
663   * Method remove removes the values specified by the given pos array and returns a new Tuple containing the
664   * removed values.
665   *
666   * @param pos of type int[]
667   * @return Tuple
668   */
669  public Tuple remove( int[] pos )
670    {
671    verifyModifiable();
672
673    // calculate offsets to apply when removing values from elements
674    int offset[] = new int[ pos.length ];
675
676    for( int i = 0; i < pos.length; i++ )
677      {
678      offset[ i ] = 0;
679
680      for( int j = 0; j < i; j++ )
681        {
682        if( pos[ j ] < pos[ i ] )
683          offset[ i ]++;
684        }
685      }
686
687    Tuple results = new Tuple();
688
689    for( int i = 0; i < pos.length; i++ )
690      results.add( elements.remove( pos[ i ] - offset[ i ] ) );
691
692    return results;
693    }
694
695  /**
696   * Method remove removes the values specified by the given selector. The declarator declares the fields in this instance.
697   *
698   * @param declarator of type Fields
699   * @param selector   of type Fields
700   * @return Tuple
701   */
702  public Tuple remove( Fields declarator, Fields selector )
703    {
704    return remove( getPos( declarator, selector ) );
705    }
706
707  /**
708   * Creates a new Tuple from the given positions, but sets the values in the current tuple to null.
709   *
710   * @param pos of type int[]
711   * @return Tuple
712   */
713  Tuple extract( int[] pos )
714    {
715    Tuple results = new Tuple();
716
717    for( int i : pos )
718      results.add( elements.set( i, null ) );
719
720    return results;
721    }
722
723  Tuple nulledCopy( int[] pos )
724    {
725    if( pos == null )
726      return size( size() );
727
728    Tuple results = new Tuple( this );
729
730    for( int i : pos )
731      results.set( i, null );
732
733    return results;
734    }
735
736  /**
737   * Sets the values in the given positions to the values from the given Tuple.
738   *
739   * @param pos   of type int[]
740   * @param tuple of type Tuple
741   */
742  void set( int[] pos, Tuple tuple )
743    {
744    verifyModifiable();
745
746    if( pos.length != tuple.size() )
747      throw new TupleException( "given tuple not same size as position array: " + pos.length + ", tuple: " + tuple.print() );
748
749    int count = 0;
750    for( int i : pos )
751      elements.set( i, tuple.elements.get( count++ ) );
752    }
753
754  private void set( int[] pos, Type[] types, Tuple tuple, CoercibleType[] coercions )
755    {
756    verifyModifiable();
757
758    if( pos.length != tuple.size() )
759      throw new TupleException( "given tuple not same size as position array: " + pos.length + ", tuple: " + tuple.print() );
760
761    int count = 0;
762
763    for( int i : pos )
764      {
765      Object element = tuple.elements.get( count );
766
767      if( types != null )
768        {
769        Type type = types[ i ];
770        element = coercions[ count ].coerce( element, type );
771        }
772
773      elements.set( i, element );
774
775      count++;
776      }
777    }
778
779  /**
780   * Method set sets the values in the given selector positions to the values from the given Tuple.
781   * <p/>
782   * If type information is given, each incoming value from tuple will be coerced from its canonical type to the
783   * current type as declared in the declarator.
784   *
785   * @param declarator of type Fields
786   * @param selector   of type Fields
787   * @param tuple      of type Tuple
788   */
789  public void set( Fields declarator, Fields selector, Tuple tuple )
790    {
791    try
792      {
793      set( declarator.getPos( selector ), declarator.getTypes(), tuple, TupleEntry.getCoercions( declarator, tuple ) );
794      }
795    catch( Exception exception )
796      {
797      throw new TupleException( "unable to set into: " + declarator.print() + ", using selector: " + selector.print(), exception );
798      }
799    }
800
801  protected void set( Fields declarator, Fields selector, Tuple tuple, CoercibleType[] coercions )
802    {
803    try
804      {
805      set( declarator.getPos( selector ), declarator.getTypes(), tuple, coercions );
806      }
807    catch( Exception exception )
808      {
809      throw new TupleException( "unable to set into: " + declarator.print() + ", using selector: " + selector.print(), exception );
810      }
811    }
812
813  /**
814   * Method iterator returns an {@link Iterator} over this Tuple instances values.
815   *
816   * @return Iterator
817   */
818  public Iterator<Object> iterator()
819    {
820    return elements.iterator();
821    }
822
823  /**
824   * Method isEmpty returns true if this Tuple instance has no values.
825   *
826   * @return the empty (type boolean) of this Tuple object.
827   */
828  public boolean isEmpty()
829    {
830    return elements.isEmpty();
831    }
832
833  /**
834   * Method size returns the number of values in this Tuple instance.
835   *
836   * @return int
837   */
838  public int size()
839    {
840    return elements.size();
841    }
842
843  /**
844   * Method elements returns a new Object[] array of this Tuple instances values.
845   *
846   * @return Object[]
847   */
848  private Object[] elements()
849    {
850    return elements.toArray();
851    }
852
853  /**
854   * Method elements returns the given destination array with the values of This tuple instance.
855   *
856   * @param destination of type Object[]
857   * @return Object[]
858   */
859  <T> T[] elements( T[] destination )
860    {
861    return elements.toArray( destination );
862    }
863
864  /**
865   * Method getTypes returns an array of the element classes. Null if the element is null.
866   *
867   * @return the types (type Class[]) of this Tuple object.
868   */
869  public Class[] getTypes()
870    {
871    Class[] types = new Class[ elements.size() ];
872
873    for( int i = 0; i < elements.size(); i++ )
874      {
875      Object value = elements.get( i );
876
877      if( value != null )
878        types[ i ] = value.getClass();
879      }
880
881    return types;
882    }
883
884  /**
885   * Method append appends all the values of the given Tuple instances to a copy of this instance.
886   *
887   * @param tuples of type Tuple
888   * @return Tuple
889   */
890  public Tuple append( Tuple... tuples )
891    {
892    Tuple result = new Tuple( this );
893
894    for( Tuple tuple : tuples )
895      result.addAll( tuple );
896
897    return result;
898    }
899
900  /**
901   * Method compareTo compares this Tuple to the given Tuple instance.
902   *
903   * @param other of type Tuple
904   * @return int
905   */
906  public int compareTo( Tuple other )
907    {
908    if( other == null || other.elements == null )
909      return 1;
910
911    if( other.elements.size() != this.elements.size() )
912      return this.elements.size() - other.elements.size();
913
914    for( int i = 0; i < this.elements.size(); i++ )
915      {
916      Comparable lhs = (Comparable) this.elements.get( i );
917      Comparable rhs = (Comparable) other.elements.get( i );
918
919      if( lhs == null && rhs == null )
920        continue;
921
922      if( lhs == null )
923        return -1;
924      else if( rhs == null )
925        return 1;
926
927      int c = lhs.compareTo( rhs ); // guaranteed to not be null
928      if( c != 0 )
929        return c;
930      }
931
932    return 0;
933    }
934
935  public int compareTo( Comparator[] comparators, Tuple other )
936    {
937    if( comparators == null )
938      return compareTo( other );
939
940    if( other == null || other.elements == null )
941      return 1;
942
943    if( other.elements.size() != this.elements.size() )
944      return this.elements.size() - other.elements.size();
945
946    if( comparators.length != this.elements.size() )
947      throw new IllegalArgumentException( "comparator array not same size as tuple elements" );
948
949    for( int i = 0; i < this.elements.size(); i++ )
950      {
951      Object lhs = this.elements.get( i );
952      Object rhs = other.elements.get( i );
953
954      int c;
955
956      if( comparators[ i ] != null )
957        c = comparators[ i ].compare( lhs, rhs );
958      else if( lhs == null && rhs == null )
959        c = 0;
960      else if( lhs == null )
961        return -1;
962      else if( rhs == null )
963        return 1;
964      else
965        c = ( (Comparable) lhs ).compareTo( rhs ); // guaranteed to not be null
966
967      if( c != 0 )
968        return c;
969      }
970
971    return 0;
972    }
973
974  /**
975   * Method compareTo implements the {@link Comparable#compareTo(Object)} method.
976   *
977   * @param other of type Object
978   * @return int
979   */
980  public int compareTo( Object other )
981    {
982    if( other instanceof Tuple )
983      return compareTo( (Tuple) other );
984    else
985      return -1;
986    }
987
988  @SuppressWarnings({"ForLoopReplaceableByForEach"})
989  @Override
990  public boolean equals( Object object )
991    {
992    if( !( object instanceof Tuple ) )
993      return false;
994
995    Tuple other = (Tuple) object;
996
997    if( this.elements.size() != other.elements.size() )
998      return false;
999
1000    for( int i = 0; i < this.elements.size(); i++ )
1001      {
1002      Object lhs = this.elements.get( i );
1003      Object rhs = other.elements.get( i );
1004
1005      if( lhs == null && rhs == null )
1006        continue;
1007
1008      if( lhs == null || rhs == null )
1009        return false;
1010
1011      if( !lhs.equals( rhs ) )
1012        return false;
1013      }
1014
1015    return true;
1016    }
1017
1018  @Override
1019  public int hashCode()
1020    {
1021    int hash = 1;
1022
1023    for( Object element : elements )
1024      hash = 31 * hash + ( element != null ? element.hashCode() : 0 );
1025
1026    return hash;
1027    }
1028
1029  @Override
1030  public String toString()
1031    {
1032    return Util.join( elements, printDelim, true );
1033    }
1034
1035  /**
1036   * Method toString writes this Tuple instance values out to a String delimited by the given String value.
1037   *
1038   * @param delim of type String
1039   * @return String
1040   */
1041  public String toString( String delim )
1042    {
1043    return Util.join( elements, delim, true );
1044    }
1045
1046  /**
1047   * Method toString writes this Tuple instance values out to a String delimited by the given String value.
1048   *
1049   * @param delim     of type String
1050   * @param printNull of type boolean
1051   * @return String
1052   */
1053  public String toString( String delim, boolean printNull )
1054    {
1055    return Util.join( elements, delim, printNull );
1056    }
1057
1058  /**
1059   * Method format uses the {@link Formatter} class for formatting this tuples values into a new string.
1060   *
1061   * @param format of type String
1062   * @return String
1063   */
1064  public String format( String format )
1065    {
1066    return String.format( format, elements() );
1067    }
1068
1069  /**
1070   * Method print returns a parsable String representation of this Tuple instance.
1071   *
1072   * @return String
1073   */
1074  public String print()
1075    {
1076    return printTo( new StringBuffer() ).toString();
1077    }
1078
1079  public StringBuffer printTo( StringBuffer buffer )
1080    {
1081    buffer.append( "[" );
1082
1083    if( elements != null )
1084      {
1085      for( int i = 0; i < elements.size(); i++ )
1086        {
1087        Object element = elements.get( i );
1088
1089        if( element instanceof Tuple )
1090          ( (Tuple) element ).printTo( buffer );
1091        else if( element == null ) // don't quote nulls to distinguish from null strings
1092          buffer.append( element );
1093        else
1094          buffer.append( "\'" ).append( element ).append( "\'" );
1095
1096        if( i < elements.size() - 1 )
1097          buffer.append( ", " );
1098        }
1099      }
1100
1101    buffer.append( "]" );
1102
1103    return buffer;
1104    }
1105
1106  private final void verifyModifiable()
1107    {
1108    if( isUnmodifiable )
1109      throw new UnsupportedOperationException( "this tuple is unmodifiable" );
1110    }
1111  }