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