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