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