001/* 002 * Copyright (c) 2016-2018 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.lang.reflect.Type; 026import java.util.Arrays; 027import java.util.Iterator; 028 029import cascading.tuple.coerce.Coercions; 030import cascading.tuple.type.CoercibleType; 031import cascading.util.ForeverValueIterator; 032 033/** 034 * Class TupleEntry allows a {@link Tuple} instance and its declaring {@link Fields} instance to be used as a single object. 035 * <p> 036 * Once a TupleEntry is created, its Fields cannot be changed, but the Tuple instance it holds can be replaced or 037 * modified. The managed Tuple should not have elements added or removed, as this will break the relationship with 038 * the associated Fields instance. 039 * <p> 040 * If type information is provided on the Fields instance, all setters on this class will use that information to 041 * coerce the given object to the expected type. 042 * <p> 043 * For example, if position is is of type {@code long}, then {@code entry.setString(0, "9" )} will coerce the "9" to a 044 * long {@code 9}. Thus, {@code entry.getObject(0) == 9l}. 045 * <p> 046 * No coercion is performed with the {@link #getObject(Comparable)} and {@link #getObject(int)} methods. 047 * <p> 048 * To set a value without coercion, see the {@link #setRaw(Comparable, Object)} and {@link #setRaw(int, Object)} 049 * methods. 050 * 051 * @see Fields 052 * @see Tuple 053 */ 054public class TupleEntry 055 { 056 private static final CoercibleType[] EMPTY_COERCIONS = new CoercibleType[ 0 ]; 057 private static final ForeverValueIterator<CoercibleType> OBJECT_ITERATOR = new ForeverValueIterator<CoercibleType>( Coercions.OBJECT ); 058 059 /** An EMPTY TupleEntry instance for use as a stand in instead of a {@code null}. */ 060 public static final TupleEntry NULL = new TupleEntry( Fields.NONE, Tuple.NULL ); 061 062 /** Field fields */ 063 private Fields fields; 064 065 private CoercibleType[] coercions = EMPTY_COERCIONS; 066 067 /** Field isUnmodifiable */ 068 private boolean isUnmodifiable = false; 069 /** Field tuple */ 070 Tuple tuple; 071 072 /** 073 * Method select will select a new Tuple instance from the given set of entries. Entries order is significant to 074 * the selector. 075 * 076 * @param selector of type Fields 077 * @param entries of type TupleEntry 078 * @return Tuple 079 */ 080 public static Tuple select( Fields selector, TupleEntry... entries ) 081 { 082 // todo: consider just appending tuples values and just peeking those values 083 Tuple result = null; 084 085 // does not do field checks 086 if( selector.isAll() ) 087 { 088 for( TupleEntry entry : entries ) 089 { 090 if( result == null ) 091 result = entry.getTuple(); 092 else 093 result = result.append( entry.getTuple() ); 094 } 095 096 return result; 097 } 098 099 int size = 0; 100 101 for( TupleEntry entry : entries ) 102 size += entry.size(); 103 104 result = Tuple.size( selector.size() ); 105 106 int offset = 0; 107 108 for( TupleEntry entry : entries ) 109 { 110 for( int i = 0; i < selector.size(); i++ ) 111 { 112 Comparable field = selector.get( i ); 113 114 int pos; 115 116 if( field instanceof String ) 117 { 118 pos = entry.fields.indexOfSafe( field ); 119 120 if( pos == -1 ) 121 continue; 122 } 123 else 124 { 125 pos = entry.fields.translatePos( (Integer) field, size ) - offset; 126 127 if( pos >= entry.size() || pos < 0 ) 128 continue; 129 } 130 131 result.set( i, entry.getObject( pos ) ); // last in wins 132 } 133 134 offset += entry.size(); 135 } 136 137 return result; 138 } 139 140 /** Constructor TupleEntry creates a new TupleEntry instance. */ 141 public TupleEntry() 142 { 143 this.fields = Fields.NONE; 144 145 setCoercions(); 146 } 147 148 /** 149 * Constructor TupleEntry creates a new TupleEntry instance. 150 * 151 * @param isUnmodifiable of type boolean 152 */ 153 @ConstructorProperties({"isUnmodifiable"}) 154 public TupleEntry( boolean isUnmodifiable ) 155 { 156 this.fields = Fields.NONE; 157 this.isUnmodifiable = isUnmodifiable; 158 159 setCoercions(); 160 } 161 162 /** 163 * Constructor TupleEntry creates a new TupleEntry instance. 164 * 165 * @param fields of type Fields 166 */ 167 @ConstructorProperties({"fields"}) 168 public TupleEntry( Fields fields ) 169 { 170 if( fields == null ) 171 throw new IllegalArgumentException( "fields may not be null" ); 172 173 this.fields = fields; 174 175 setCoercions(); 176 } 177 178 /** 179 * Constructor TupleEntry creates a new TupleEntry instance. 180 * 181 * @param fields of type Fields 182 * @param isUnmodifiable of type boolean 183 */ 184 @ConstructorProperties({"fields", "isUnmodifiable"}) 185 public TupleEntry( Fields fields, boolean isUnmodifiable ) 186 { 187 if( fields == null ) 188 throw new IllegalArgumentException( "fields may not be null" ); 189 190 this.fields = fields; 191 this.isUnmodifiable = isUnmodifiable; 192 193 setCoercions(); 194 } 195 196 /** 197 * Constructor TupleEntry creates a new TupleEntry instance. 198 * 199 * @param fields of type Fields 200 * @param tuple of type Tuple 201 * @param isUnmodifiable of type boolean 202 */ 203 @ConstructorProperties({"fields", "tuple", "isUnmodifiable"}) 204 public TupleEntry( Fields fields, Tuple tuple, boolean isUnmodifiable ) 205 { 206 if( fields == null ) 207 throw new IllegalArgumentException( "fields may not be null" ); 208 209 this.fields = fields; 210 this.isUnmodifiable = isUnmodifiable; 211 setTuple( tuple ); 212 213 setCoercions(); 214 } 215 216 /** 217 * Constructor TupleEntry creates a new TupleEntry instance. 218 * 219 * @param fields of type Fields 220 * @param tuple of type Tuple 221 */ 222 @ConstructorProperties({"fields", "tuple"}) 223 public TupleEntry( Fields fields, Tuple tuple ) 224 { 225 if( fields == null ) 226 throw new IllegalArgumentException( "fields may not be null" ); 227 228 this.fields = fields; 229 this.tuple = tuple; 230 231 setCoercions(); 232 } 233 234 /** 235 * Constructor TupleEntry creates a new TupleEntry instance that is a safe copy of the given tupleEntry. 236 * <p> 237 * The new instance is safe to cache and will be modifiable regardless of the given tupleEntry state. 238 * 239 * @param tupleEntry of type TupleEntry 240 */ 241 @ConstructorProperties({"tupleEntry"}) 242 public TupleEntry( TupleEntry tupleEntry ) 243 { 244 if( tupleEntry == null ) 245 throw new IllegalArgumentException( "tupleEntry may not be null" ); 246 247 this.fields = tupleEntry.getFields(); 248 this.tuple = tupleEntry.getTupleCopy(); 249 250 setCoercions(); 251 } 252 253 /** 254 * Constructor TupleEntry creates a new TupleEntry instance. 255 * 256 * @param tuple of type Tuple 257 */ 258 @ConstructorProperties({"tuple"}) 259 public TupleEntry( Tuple tuple ) 260 { 261 if( tuple == null ) 262 throw new IllegalArgumentException( "tuple may not be null" ); 263 264 this.fields = Fields.size( tuple.size() ); 265 this.tuple = tuple; 266 267 setCoercions(); 268 } 269 270 private void setCoercions() 271 { 272 if( coercions != EMPTY_COERCIONS ) 273 return; 274 275 coercions = getCoercions( getFields(), tuple ); 276 } 277 278 static CoercibleType[] getCoercions( Fields fields, Tuple tuple ) 279 { 280 Type[] types = fields.types; // safe to not get a copy 281 int size = fields.size(); 282 283 size = size == 0 && tuple != null ? tuple.size() : size; 284 285 if( size == 0 ) 286 return EMPTY_COERCIONS; 287 288 return Coercions.coercibleArray( size, types ); 289 } 290 291 /** 292 * Method isUnmodifiable returns true if this TupleEntry is unmodifiable. 293 * 294 * @return boolean 295 */ 296 public boolean isUnmodifiable() 297 { 298 return isUnmodifiable; 299 } 300 301 /** 302 * Method getFields returns the fields of this TupleEntry object. 303 * 304 * @return the fields (type Fields) of this TupleEntry object. 305 */ 306 public Fields getFields() 307 { 308 return fields; 309 } 310 311 /** 312 * Returns true if there are types associated with this instance. 313 * 314 * @return boolean 315 */ 316 public boolean hasTypes() 317 { 318 return fields.hasTypes(); 319 } 320 321 /** 322 * Method getTuple returns the tuple of this TupleEntry object. 323 * 324 * @return the tuple (type Tuple) of this TupleEntry object. 325 */ 326 public Tuple getTuple() 327 { 328 return tuple; 329 } 330 331 /** 332 * Method getTupleCopy returns a copy of the tuple of this TupleEntry object. 333 * 334 * @return a copy of the tuple (type Tuple) of this TupleEntry object. 335 */ 336 public Tuple getTupleCopy() 337 { 338 return new Tuple( tuple ); 339 } 340 341 /** 342 * Method getCoercedTuple is a helper method for copying the current tuple elements into a new Tuple, 343 * of the same size, as the requested coerced types. 344 * 345 * @param types of type Type[] 346 * @return returns the a new Tuple instance with coerced values 347 */ 348 public Tuple getCoercedTuple( Type[] types ) 349 { 350 return getCoercedTuple( types, Tuple.size( types.length ) ); 351 } 352 353 /** 354 * Method getCoercedTuple is a helper method for copying the current tuple elements into the new Tuple, 355 * of the same size, as the requested coerced types. 356 * 357 * @param types of type Type[] 358 * @param into of type Tuple 359 * @return returns the given into Tuple instance with coerced values 360 */ 361 public Tuple getCoercedTuple( Type[] types, Tuple into ) 362 { 363 if( into == null ) 364 throw new IllegalArgumentException( "into argument Tuple may not be null" ); 365 366 if( coercions.length != types.length || types.length != into.size() ) 367 throw new IllegalArgumentException( "current entry and given tuple and types must be same length" ); 368 369 for( int i = 0; i < coercions.length; i++ ) 370 { 371 Object element = tuple.getObject( i ); 372 into.set( i, Coercions.coerce( coercions[ i ], element, types[ i ] ) ); 373 } 374 375 return into; 376 } 377 378 /** 379 * Method setTuple sets the tuple of this TupleEntry object, no copy will be performed. 380 * <p> 381 * If the given tuple is "unmodifiable" ({@code Tuple.isUnmodifiable() == true}) and this TupleEntry is 382 * not "unmodifiable", an exception will be thrown. 383 * <p> 384 * Unmodifiable tuples are generally owned by the system and cannot be be changed and must not be cached. 385 * 386 * @param tuple the tuple of this TupleEntry object. 387 */ 388 public void setTuple( Tuple tuple ) 389 { 390 if( !isUnmodifiable && tuple != null && tuple.isUnmodifiable() ) 391 throw new IllegalArgumentException( "current entry is modifiable but given tuple is not modifiable, make copy of given Tuple first" ); 392 393 if( tuple != null && isUnmodifiable ) 394 this.tuple = Tuples.asUnmodifiable( tuple ); 395 else 396 this.tuple = tuple; 397 398 setCoercions(); 399 } 400 401 /** 402 * Method setCanonicalTuple replaces each value of the current tuple with the given tuple elements after 403 * they are coerced. 404 * <p> 405 * This method will modify the existing Tuple wrapped by this TupleEntry instance even 406 * if it is marked as unmodifiable. 407 * <p> 408 * If tuple argument is {@code null}, the current tuple will be set to {@code null}. 409 * 410 * @param tuple to replace the current wrapped Tuple instance 411 */ 412 public void setCanonicalTuple( Tuple tuple ) 413 { 414 if( tuple == null ) 415 { 416 this.tuple = null; 417 return; 418 } 419 420 if( isUnmodifiable ) 421 tuple = Tuples.asUnmodifiable( tuple ); 422 423 if( fields.size() != tuple.size() ) 424 throw new IllegalArgumentException( "current entry and given tuple must be same length" ); 425 426 for( int i = 0; i < coercions.length; i++ ) 427 { 428 Object element = tuple.getObject( i ); 429 430 this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type 431 } 432 } 433 434 /** 435 * Method setCanonicalValues replaces each value of the current tuple with th give Object[] 436 * after they are coerced. 437 * 438 * @param values to replace the current wrapped tuple instance values 439 */ 440 public void setCanonicalValues( Object[] values ) 441 { 442 setCanonicalValues( values, 0, values.length ); 443 } 444 445 /** 446 * Method setCanonicalValues replaces each value of the current tuple with th give Object[] 447 * after they are coerced. 448 * 449 * @param values to replace the current wrapped tuple instance values 450 * @param offset index to being the copy 451 * @param length the number of elements to copy 452 */ 453 public void setCanonicalValues( Object[] values, int offset, int length ) 454 { 455 if( fields.size() != length ) 456 throw new IllegalArgumentException( "current entry and given array must be same length" ); 457 458 for( int i = offset; i < coercions.length; i++ ) 459 { 460 Object element = values[ i ]; 461 462 this.tuple.set( i, coercions[ i ].canonical( element ) ); // force read type to the expected type 463 } 464 } 465 466 /** 467 * Method size returns the number of values in this instance. 468 * 469 * @return int 470 */ 471 public int size() 472 { 473 return tuple.size(); 474 } 475 476 /** 477 * Method getObject returns the value in the given position pos. 478 * <p> 479 * No coercion is performed if there is an associated coercible type. 480 * 481 * @param pos position of the element to return. 482 * @return Object 483 */ 484 public Object getObject( int pos ) 485 { 486 return tuple.getObject( pos ); 487 } 488 489 /** 490 * Method getObject returns the value in the given field or position as the requested type. 491 * <p> 492 * Coercion is performed to the given type. 493 * 494 * @param pos position of the element to return. 495 * @return Object 496 */ 497 public Object getObject( int pos, Type type ) 498 { 499 if( pos > coercions.length - 1 ) 500 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 501 502 return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type ); 503 } 504 505 /** 506 * Method getObject returns the value in the given field or position. 507 * <br> 508 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 509 * be considered. 510 * <p> 511 * No coercion is performed if there is an associated coercible type. 512 * 513 * @param fieldName field name or position to return 514 * @return Comparable 515 */ 516 public Object getObject( Comparable fieldName ) 517 { 518 int pos = fields.getPos( asFieldName( fieldName ) ); 519 return tuple.getObject( pos ); 520 } 521 522 /** 523 * Method getObject returns the value in the given field or position as the requested type. 524 * <br> 525 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 526 * be considered. 527 * <p> 528 * Coercion is performed to the given type. 529 * 530 * @param fieldName field name or position to return 531 * @return Comparable 532 */ 533 public Object getObject( Comparable fieldName, Type type ) 534 { 535 int pos = fields.getPos( asFieldName( fieldName ) ); 536 537 if( pos > coercions.length - 1 ) 538 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 539 540 return Coercions.coerce( coercions[ pos ], tuple.getObject( pos ), type ); 541 } 542 543 /** 544 * Method set sets the value in the given position. 545 * <p> 546 * No coercion is performed if there is an associated coercible type. 547 * 548 * @param pos position to set 549 * @param value of type Comparable 550 */ 551 public void setRaw( int pos, Object value ) 552 { 553 tuple.set( pos, value ); 554 } 555 556 /** 557 * Method set sets the value in the given field or position. 558 * <p> 559 * No coercion is performed if there is an associated coercible type. 560 * 561 * @param fieldName field name or position to set 562 * @param value of type Comparable 563 */ 564 public void setRaw( Comparable fieldName, Object value ) 565 { 566 tuple.set( fields.getPos( asFieldName( fieldName ) ), value ); 567 } 568 569 /** 570 * Method set sets the value in the given field or position. 571 * 572 * @param fieldName field name or position to set 573 * @param value of type Comparable 574 */ 575 public void setObject( Comparable fieldName, Object value ) 576 { 577 int pos = fields.getPos( asFieldName( fieldName ) ); 578 579 if( pos > coercions.length - 1 ) 580 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 581 582 tuple.set( pos, coercions[ pos ].canonical( value ) ); 583 } 584 585 /** 586 * Method setBoolean sets the value in the given field or position. 587 * 588 * @param fieldName field name or position to set 589 * @param value of type boolean 590 */ 591 public void setBoolean( Comparable fieldName, boolean value ) 592 { 593 int pos = fields.getPos( asFieldName( fieldName ) ); 594 595 if( pos > coercions.length - 1 ) 596 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 597 598 tuple.set( pos, coercions[ pos ].canonical( value ) ); 599 } 600 601 /** 602 * Method setShort sets the value in the given field or position. 603 * 604 * @param fieldName field name or position to set 605 * @param value of type short 606 */ 607 public void setShort( Comparable fieldName, short value ) 608 { 609 int pos = fields.getPos( asFieldName( fieldName ) ); 610 611 if( pos > coercions.length - 1 ) 612 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 613 614 tuple.set( pos, coercions[ pos ].canonical( value ) ); 615 } 616 617 /** 618 * Method setInteger sets the value in the given field or position. 619 * 620 * @param fieldName field name or position to set 621 * @param value of type int 622 */ 623 public void setInteger( Comparable fieldName, int value ) 624 { 625 int pos = fields.getPos( asFieldName( fieldName ) ); 626 627 if( pos > coercions.length - 1 ) 628 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 629 630 tuple.set( pos, coercions[ pos ].canonical( value ) ); 631 } 632 633 /** 634 * Method setLong sets the value in the given field or position. 635 * 636 * @param fieldName field name or position to set 637 * @param value of type long 638 */ 639 public void setLong( Comparable fieldName, long value ) 640 { 641 int pos = fields.getPos( asFieldName( fieldName ) ); 642 643 if( pos > coercions.length - 1 ) 644 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 645 646 tuple.set( pos, coercions[ pos ].canonical( value ) ); 647 } 648 649 /** 650 * Method setFloat sets the value in the given field or position. 651 * 652 * @param fieldName field name or position to set 653 * @param value of type float 654 */ 655 public void setFloat( Comparable fieldName, float value ) 656 { 657 int pos = fields.getPos( asFieldName( fieldName ) ); 658 659 if( pos > coercions.length - 1 ) 660 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 661 662 tuple.set( pos, coercions[ pos ].canonical( value ) ); 663 } 664 665 /** 666 * Method setDouble sets the value in the given field or position. 667 * 668 * @param fieldName field name or position to set 669 * @param value of type double 670 */ 671 public void setDouble( Comparable fieldName, double value ) 672 { 673 int pos = fields.getPos( asFieldName( fieldName ) ); 674 675 if( pos > coercions.length - 1 ) 676 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 677 678 tuple.set( pos, coercions[ pos ].canonical( value ) ); 679 } 680 681 /** 682 * Method setString sets the value in the given field or position. 683 * 684 * @param fieldName field name or position to set 685 * @param value of type String 686 */ 687 public void setString( Comparable fieldName, String value ) 688 { 689 int pos = fields.getPos( asFieldName( fieldName ) ); 690 691 if( pos > coercions.length - 1 ) 692 throw new TupleException( "position value is too large: " + pos + ", positions in field: " + tuple.size() ); 693 694 tuple.set( pos, coercions[ pos ].canonical( value ) ); 695 } 696 697 /** 698 * Method getString returns the element for the given field name or position as a String. 699 * <br> 700 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 701 * be considered. 702 * 703 * @param fieldName field name or position to return 704 * @return String 705 */ 706 public String getString( Comparable fieldName ) 707 { 708 return (String) getObject( fieldName, String.class ); 709 } 710 711 /** 712 * Method getFloat returns the element for the given field name or position as a float. Zero if null. 713 * <br> 714 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 715 * be considered. 716 * 717 * @param fieldName field name or position to return 718 * @return float 719 */ 720 public float getFloat( Comparable fieldName ) 721 { 722 return (Float) getObject( fieldName, float.class ); 723 } 724 725 /** 726 * Method getDouble returns the element for the given field name or position as a double. Zero if null. 727 * <br> 728 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 729 * be considered. 730 * 731 * @param fieldName field name or position to return 732 * @return double 733 */ 734 public double getDouble( Comparable fieldName ) 735 { 736 return (Double) getObject( fieldName, double.class ); 737 } 738 739 /** 740 * Method getInteger returns the element for the given field name or position as an int. Zero if null. 741 * <br> 742 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 743 * be considered. 744 * 745 * @param fieldName field name or position to return 746 * @return int 747 */ 748 public int getInteger( Comparable fieldName ) 749 { 750 return (Integer) getObject( fieldName, int.class ); 751 } 752 753 /** 754 * Method getLong returns the element for the given field name or position as a long. Zero if null. 755 * <br> 756 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 757 * be considered. 758 * 759 * @param fieldName field name or position to return 760 * @return long 761 */ 762 public long getLong( Comparable fieldName ) 763 { 764 return (Long) getObject( fieldName, long.class ); 765 } 766 767 /** 768 * Method getShort returns the element for the given field name or position as a short. Zero if null. 769 * <br> 770 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 771 * be considered. 772 * 773 * @param fieldName field name or position to return 774 * @return short 775 */ 776 public short getShort( Comparable fieldName ) 777 { 778 return (Short) getObject( fieldName, short.class ); 779 } 780 781 /** 782 * Method getBoolean returns the element for the given field name or position as a boolean. 783 * If the value is (case ignored) the string 'true', a {@code true} value will be returned. {@code false} if null. 784 * <br> 785 * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will 786 * be considered. 787 * 788 * @param fieldName field name or position to return 789 * @return boolean 790 */ 791 public boolean getBoolean( Comparable fieldName ) 792 { 793 return (Boolean) getObject( fieldName, boolean.class ); 794 } 795 796 private Comparable asFieldName( Comparable fieldName ) 797 { 798 return Fields.asFieldName( fieldName ); 799 } 800 801 /** 802 * Method selectEntry selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the 803 * same fields as declared are given, {@code this} will be returned. 804 * <p> 805 * The returned TupleEntry will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance. 806 * <p> 807 * See {@link #selectEntryCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection. 808 * <p> 809 * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent 810 * on the given selector. 811 * 812 * @param selector Fields selector that selects the values to return 813 * @return TupleEntry 814 */ 815 public TupleEntry selectEntry( Fields selector ) 816 { 817 if( selector == null || selector.isAll() || fields == selector ) // == is intentional 818 return this; 819 820 if( selector.isNone() ) 821 return isUnmodifiable ? TupleEntry.NULL : new TupleEntry(); 822 823 return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ), isUnmodifiable ); 824 } 825 826 /** 827 * Method selectEntry selects the fields specified in selector from this instance. 828 * <p> 829 * It is guaranteed to return a new modifiable TupleEntry instance at a cost of copying data. 830 * <p> 831 * The returned instance is safe to cache. 832 * 833 * @param selector Fields selector that selects the values to return 834 * @return TupleEntry 835 */ 836 public TupleEntry selectEntryCopy( Fields selector ) 837 { 838 if( selector == null || selector.isAll() || fields == selector ) // == is intentional 839 return new TupleEntry( this ); 840 841 if( selector.isNone() ) 842 return new TupleEntry(); 843 844 return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ) ); 845 } 846 847 /** 848 * Method selectTuple selects the fields specified in the selector from this instance. If {@link Fields#ALL} or the 849 * same fields as declared are given, {@code this.getTuple()} will be returned. 850 * <p> 851 * The returned Tuple will be either modifiable or unmodifiable, depending on the state of this TupleEntry instance. 852 * <p> 853 * See {@link #selectTupleCopy(Fields)} to guarantee a copy suitable for modifying or caching/storing in a collection. 854 * <p> 855 * Note this is a bug fix and change from 2.0 and 2.1. In previous versions the modifiable state was dependent 856 * on the given selector. 857 * 858 * @param selector Fields selector that selects the values to return 859 * @return Tuple 860 */ 861 public Tuple selectTuple( Fields selector ) 862 { 863 if( selector == null || selector.isAll() || fields == selector ) // == is intentional 864 return this.tuple; 865 866 if( selector.isNone() ) 867 return isUnmodifiable ? Tuple.NULL : new Tuple(); 868 869 Tuple result = tuple.get( fields, selector ); 870 871 if( isUnmodifiable ) 872 Tuples.asUnmodifiable( result ); 873 874 return result; 875 } 876 877 /** 878 * Method selectTupleCopy selects the fields specified in selector from this instance. 879 * <p> 880 * It is guaranteed to return a new modifiable Tuple instance at a cost of copying data. 881 * <p> 882 * The returned instance is safe to cache. 883 * 884 * @param selector Fields selector that selects the values to return 885 * @return Tuple 886 */ 887 public Tuple selectTupleCopy( Fields selector ) 888 { 889 if( selector == null || selector.isAll() || fields == selector ) // == is intentional 890 return new Tuple( this.tuple ); 891 892 if( selector.isNone() ) 893 return new Tuple(); 894 895 return tuple.get( fields, selector ); 896 } 897 898 /** 899 * Method selectInto selects the fields specified in the selector from this instance and copies 900 * them into the given tuple argument. 901 * 902 * @param selector of type Fields 903 * @param tuple of type Tuple 904 * @return returns the given tuple argument with new values added 905 */ 906 public Tuple selectInto( Fields selector, Tuple tuple ) 907 { 908 if( selector.isNone() ) 909 return tuple; 910 911 int[] pos = this.tuple.getPos( fields, selector ); 912 913 if( pos == null || pos.length == 0 ) 914 { 915 tuple.addAll( this.tuple ); 916 } 917 else 918 { 919 for( int i : pos ) 920 tuple.add( this.tuple.getObject( i ) ); 921 } 922 923 return tuple; 924 } 925 926 /** 927 * Method setTuple sets the values specified by the selector to the values given by the given tuple, the given 928 * values will always be copied into this TupleEntry. 929 * 930 * @param selector of type Fields 931 * @param tuple of type Tuple 932 */ 933 public void setTuple( Fields selector, Tuple tuple ) 934 { 935 if( selector == null || selector.isAll() ) 936 this.tuple.setAll( tuple ); 937 else 938 this.tuple.set( fields, selector, tuple ); 939 } 940 941 /** 942 * Method set sets the values from the given tupleEntry into this TupleEntry instance based on the given 943 * tupleEntry field names. 944 * <p> 945 * If type information is given, each incoming value will be coerced from its canonical type to the current type. 946 * 947 * @param tupleEntry of type TupleEntry 948 */ 949 public void set( TupleEntry tupleEntry ) 950 { 951 this.tuple.set( fields, tupleEntry.getFields(), tupleEntry.getTuple(), tupleEntry.coercions ); 952 } 953 954 /** 955 * Method appendNew appends the given TupleEntry instance to this instance. 956 * 957 * @param entry of type TupleEntry 958 * @return TupleEntry 959 */ 960 public TupleEntry appendNew( TupleEntry entry ) 961 { 962 Fields appendedFields = fields.append( entry.fields.isUnknown() ? Fields.size( entry.tuple.size() ) : entry.fields ); 963 Tuple appendedTuple = tuple.append( entry.tuple ); 964 965 return new TupleEntry( appendedFields, appendedTuple ); 966 } 967 968 @Override 969 public boolean equals( Object object ) 970 { 971 if( this == object ) 972 return true; 973 974 if( !( object instanceof TupleEntry ) ) 975 return false; 976 977 TupleEntry that = (TupleEntry) object; 978 979 if( fields != null ? !fields.equals( that.fields ) : that.fields != null ) 980 return false; 981 982 // use comparators if in the this side fields instance 983 if( tuple != null ? fields.compare( tuple, that.tuple ) != 0 : that.tuple != null ) 984 return false; 985 986 return true; 987 } 988 989 @Override 990 public int hashCode() 991 { 992 int result = fields != null ? fields.hashCode() : 0; 993 result = 31 * result + ( tuple != null ? tuple.hashCode() : 0 ); 994 return result; 995 } 996 997 @Override 998 public String toString() 999 { 1000 if( fields == null ) 1001 return "empty"; 1002 else if( tuple == null ) 1003 return "fields: " + fields.print(); 1004 else 1005 return "fields: " + fields.print() + " tuple: " + tuple.print(); 1006 } 1007 1008 /** 1009 * Method asIterableOf returns an {@link Iterable} instance that will coerce all Tuple elements 1010 * into the given {@code type} parameter. 1011 * <p> 1012 * This method honors any {@link cascading.tuple.type.CoercibleType} instances on the internal 1013 * Fields instance for the specified Tuple element. 1014 * 1015 * @param type of type Class 1016 * @return an Iterable 1017 */ 1018 public <T> Iterable<T> asIterableOf( final Class<T> type ) 1019 { 1020 return () -> 1021 { 1022 final Iterator<CoercibleType> coercibleIterator = coercions.length == 0 ? OBJECT_ITERATOR : Arrays.asList( coercions ).iterator(); 1023 final Iterator valuesIterator = tuple.iterator(); 1024 1025 return new Iterator<T>() 1026 { 1027 @Override 1028 public boolean hasNext() 1029 { 1030 return valuesIterator.hasNext(); 1031 } 1032 1033 @Override 1034 public T next() 1035 { 1036 Object next = valuesIterator.next(); 1037 1038 return (T) coercibleIterator.next().coerce( next, type ); 1039 } 1040 1041 @Override 1042 public void remove() 1043 { 1044 valuesIterator.remove(); 1045 } 1046 }; 1047 }; 1048 } 1049 }