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