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