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