    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 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          Type type = types[ count++ ];
    824          element = coercions[ i ].coerce( element, type );
    825
    826          elements.set( i, element );
    827          }
    828        }
    829
    830      /**
    831       * Method set sets the values in the given selector positions to the values from the given Tuple.
    832       *
    833       * @param declarator of type Fields
    834       * @param selector   of type Fields
    835       * @param tuple      of type Tuple
    836       */
    837      public void set( Fields declarator, Fields selector, Tuple tuple )
    838        {
    839        try
    840          {
    841          set( declarator.getPos( selector ), tuple );
    842          }
    843        catch( Exception exception )
    844          {
    845          throw new TupleException( "unable to set into: " + declarator.print() + ", using selector: " + selector.print(), exception );
    846          }
    847        }
    848
    849      protected void set( Fields declarator, Fields selector, Tuple tuple, CoercibleType[] coercions )
    850        {
    851        try
    852          {
    853          set( declarator.getPos( selector ), declarator.getTypes(), tuple, coercions );
    854          }
    855        catch( Exception exception )
    856          {
    857          throw new TupleException( "unable to set into: " + declarator.print() + ", using selector: " + selector.print(), exception );
    858          }
    859        }
    860
    861      /**
    862       * Method iterator returns an {@link Iterator} over this Tuple instances values.
    863       *
    864       * @return Iterator
    865       */
    866      public Iterator<Object> iterator()
    867        {
    868        return elements.iterator();
    869        }
    870
    871      /**
    872       * Method isEmpty returns true if this Tuple instance has no values.
    873       *
    874       * @return the empty (type boolean) of this Tuple object.
    875       */
    876      public boolean isEmpty()
    877        {
    878        return elements.isEmpty();
    879        }
    880
    881      /**
    882       * Method size returns the number of values in this Tuple instance.
    883       *
    884       * @return int
    885       */
    886      public int size()
    887        {
    888        return elements.size();
    889        }
    890
    891      /**
    892       * Method elements returns a new Object[] array of this Tuple instances values.
    893       *
    894       * @return Object[]
    895       */
    896      private Object[] elements()
    897        {
    898        return elements.toArray();
    899        }
    900
    901      /**
    902       * Method elements returns the given destination array with the values of This tuple instance.
    903       *
    904       * @param destination of type Object[]
    905       * @return Object[]
    906       */
    907      <T> T[] elements( T[] destination )
    908        {
    909        return elements.toArray( destination );
    910        }
    911
    912      /**
    913       * Method getTypes returns an array of the element classes. Null if the element is null.
    914       *
    915       * @return the types (type Class[]) of this Tuple object.
    916       */
    917      public Class[] getTypes()
    918        {
    919        Class[] types = new Class[ elements.size() ];
    920
    921        for( int i = 0; i < elements.size(); i++ )
    922          {
    923          Object value = elements.get( i );
    924
    925          if( value != null )
    926            types[ i ] = value.getClass();
    927          }
    928
    929        return types;
    930        }
    931
    932      /**
    933       * Method append appends all the values of the given Tuple instances to a copy of this instance.
    934       *
    935       * @param tuples of type Tuple
    936       * @return Tuple
    937       */
    938      public Tuple append( Tuple... tuples )
    939        {
    940        Tuple result = new Tuple( this );
    941
    942        for( Tuple tuple : tuples )
    943          result.addAll( tuple );
    944
    945        return result;
    946        }
    947
    948      /**
    949       * Method compareTo compares this Tuple to the given Tuple instance.
    950       *
    951       * @param other of type Tuple
    952       * @return int
    953       */
    954      public int compareTo( Tuple other )
    955        {
    956        if( other == null || other.elements == null )
    957          return 1;
    958
    959        if( other.elements.size() != this.elements.size() )
    960          return this.elements.size() - other.elements.size();
    961
    962        for( int i = 0; i < this.elements.size(); i++ )
    963          {
    964          Comparable lhs = (Comparable) this.elements.get( i );
    965          Comparable rhs = (Comparable) other.elements.get( i );
    966
    967          if( lhs == null && rhs == null )
    968            continue;
    969
    970          if( lhs == null )
    971            return -1;
    972          else if( rhs == null )
    973            return 1;
    974
    975          int c = lhs.compareTo( rhs ); // guaranteed to not be null
    976          if( c != 0 )
    977            return c;
    978          }
    979
    980        return 0;
    981        }
    982
    983      public int compareTo( Comparator[] comparators, Tuple other )
    984        {
    985        if( comparators == null )
    986          return compareTo( other );
    987
    988        if( other == null || other.elements == null )
    989          return 1;
    990
    991        if( other.elements.size() != this.elements.size() )
    992          return this.elements.size() - other.elements.size();
    993
    994        if( comparators.length != this.elements.size() )
    995          throw new IllegalArgumentException( "comparator array not same size as tuple elements" );
    996
    997        for( int i = 0; i < this.elements.size(); i++ )
    998          {
    999          Object lhs = this.elements.get( i );
    1000          Object rhs = other.elements.get( i );
    1001
    1002          int c;
    1003
    1004          if( comparators[ i ] != null )
    1005            c = comparators[ i ].compare( lhs, rhs );
    1006          else if( lhs == null && rhs == null )
    1007            c = 0;
    1008          else if( lhs == null )
    1009            return -1;
    1010          else if( rhs == null )
    1011            return 1;
    1012          else
    1013            c = ( (Comparable) lhs ).compareTo( rhs ); // guaranteed to not be null
    1014
    1015          if( c != 0 )
    1016            return c;
    1017          }
    1018
    1019        return 0;
    1020        }
    1021
    1022      /**
    1023       * Method compareTo implements the {@link Comparable#compareTo(Object)} method.
    1024       *
    1025       * @param other of type Object
    1026       * @return int
    1027       */
    1028      public int compareTo( Object other )
    1029        {
    1030        if( other instanceof Tuple )
    1031          return compareTo( (Tuple) other );
    1032        else
    1033          return -1;
    1034        }
    1035
    1036      @SuppressWarnings({"ForLoopReplaceableByForEach"})
    1037      @Override
    1038      public boolean equals( Object object )
    1039        {
    1040        if( !( object instanceof Tuple ) )
    1041          return false;
    1042
    1043        Tuple other = (Tuple) object;
    1044
    1045        if( this.elements.size() != other.elements.size() )
    1046          return false;
    1047
    1048        for( int i = 0; i < this.elements.size(); i++ )
    1049          {
    1050          Object lhs = this.elements.get( i );
    1051          Object rhs = other.elements.get( i );
    1052
    1053          if( lhs == null && rhs == null )
    1054            continue;
    1055
    1056          if( lhs == null || rhs == null )
    1057            return false;
    1058
    1059          if( !lhs.equals( rhs ) )
    1060            return false;
    1061          }
    1062
    1063        return true;
    1064        }
    1065
    1066      @Override
    1067      public int hashCode()
    1068        {
    1069        int hash = 1;
    1070
    1071        for( Object element : elements )
    1072          hash = 31 * hash + ( element != null ? element.hashCode() : 0 );
    1073
    1074        return hash;
    1075        }
    1076
    1077      @Override
    1078      public String toString()
    1079        {
    1080        return Util.join( elements, printDelim, true );
    1081        }
    1082
    1083      /**
    1084       * Method toString writes this Tuple instance values out to a String delimited by the given String value.
    1085       *
    1086       * @param delim of type String
    1087       * @return String
    1088       */
    1089      public String toString( String delim )
    1090        {
    1091        return Util.join( elements, delim, true );
    1092        }
    1093
    1094      /**
    1095       * Method toString writes this Tuple instance values out to a String delimited by the given String value.
    1096       *
    1097       * @param delim     of type String
    1098       * @param printNull of type boolean
    1099       * @return String
    1100       */
    1101      public String toString( String delim, boolean printNull )
    1102        {
    1103        return Util.join( elements, delim, printNull );
    1104        }
    1105
    1106      /**
    1107       * Method format uses the {@link Formatter} class for formatting this tuples values into a new string.
    1108       *
    1109       * @param format of type String
    1110       * @return String
    1111       */
    1112      public String format( String format )
    1113        {
    1114        return String.format( format, elements() );
    1115        }
    1116
    1117      /**
    1118       * Method print returns a parsable String representation of this Tuple instance.
    1119       *
    1120       * @return String
    1121       */
    1122      public String print()
    1123        {
    1124        return printTo( new StringBuffer() ).toString();
    1125        }
    1126
    1127      public StringBuffer printTo( StringBuffer buffer )
    1128        {
    1129        buffer.append( "[" );
    1130
    1131        if( elements != null )
    1132          {
    1133          for( int i = 0; i < elements.size(); i++ )
    1134            {
    1135            Object element = elements.get( i );
    1136
    1137            if( element instanceof Tuple )
    1138              ( (Tuple) element ).printTo( buffer );
    1139            else if( element == null ) // don't quote nulls to distinguish from null strings
    1140              buffer.append( element );
    1141            else
    1142              buffer.append( "\'" ).append( element ).append( "\'" );
    1143
    1144            if( i < elements.size() - 1 )
    1145              buffer.append( ", " );
    1146            }
    1147          }
    1148
    1149        buffer.append( "]" );
    1150
    1151        return buffer;
    1152        }
    1153
    1154      private final void verifyModifiable()
    1155        {
    1156        if( isUnmodifiable )
    1157          throw new UnsupportedOperationException( "this tuple is unmodifiable" );
    1158        }
    1159      }