    001/*
    002 * Copyright (c) 2007-2017 Xplenty, 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  }