001/** A class representing the type of a RecordToken. 002 003 Copyright (c) 1997-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.data.type; 028 029import java.util.Arrays; 030 031import ptolemy.data.FunctionToken; 032import ptolemy.data.Token; 033import ptolemy.data.TupleToken; 034import ptolemy.graph.CPO; 035import ptolemy.graph.InequalityTerm; 036import ptolemy.kernel.util.IllegalActionException; 037import ptolemy.kernel.util.InternalErrorException; 038 039/////////////////////////////////////////////////////////////////// 040//// TupleType 041 042/** 043 A class representing the type of a FunctionToken. 044 045 @author Steve Neuendorffer 046 @version $Id$ 047 @since Ptolemy II 10.0 048 @Pt.ProposedRating Red (neuendor) 049 @Pt.AcceptedRating Red (cxh) 050 */ 051public class TupleType extends StructuredType implements Cloneable { 052 /** Construct a new TupleType with the specified argument types 053 * and the given return type. To leave the types of some fields 054 * undeclared, use BaseType.UNKNOWN. To construct the type for a 055 * function of no arguments, set the length of the argument array 056 * to 0. 057 * @param types An array of Type. 058 * @exception IllegalArgumentException If the labels and types do 059 * not have the same size. 060 * @exception NullPointerException If one of the arguments is null. 061 */ 062 public TupleType(Type[] types) { 063 _elementTypeTerms = new FieldTypeTerm[types.length]; 064 065 for (int i = 0; i < types.length; i++) { 066 FieldTypeTerm fieldType = new FieldTypeTerm(types[i]); 067 _elementTypeTerms[i] = fieldType; 068 } 069 } 070 071 /////////////////////////////////////////////////////////////////// 072 //// public methods //// 073 074 /** Return a deep copy of this TupleType if it is a variable, or 075 * itself if it is a constant. 076 * @return A TupleType. 077 */ 078 @Override 079 public Object clone() { 080 if (isConstant()) { 081 return this; 082 } else { 083 // construct the labels and declared types array 084 Type[] types = new Type[_elementTypeTerms.length]; 085 086 for (int i = 0; i < types.length; i++) { 087 types[i] = getElementType(i); 088 } 089 090 TupleType newObj = new TupleType(types); 091 092 try { 093 newObj.updateType(this); 094 } catch (IllegalActionException ex) { 095 throw new InternalErrorException(null, ex, 096 "Failed to update new instance."); 097 } 098 099 return newObj; 100 } 101 } 102 103 /** Convert the argument token into an ArrayToken having this 104 * type, if losslessly conversion can be done. The argument must 105 * be an ArrayToken. 106 * @param token A token. 107 * @return An ArrayToken. 108 * @exception IllegalActionException If lossless conversion 109 * cannot be done. 110 */ 111 @Override 112 public Token convert(Token token) throws IllegalActionException { 113 if (!(token instanceof TupleToken)) { 114 throw new IllegalArgumentException( 115 Token.notSupportedIncomparableConversionMessage(token, 116 toString())); 117 } 118 119 TupleToken argumentTupleToken = (TupleToken) token; 120 121 Token[] argumentTuple = argumentTupleToken.tupleValue(); 122 Token[] resultArray = new Token[argumentTuple.length]; 123 124 if (argumentTupleToken.length() == _elementTypeTerms.length) { 125 try { 126 for (int i = 0; i < argumentTuple.length; i++) { 127 resultArray[i] = getElementType(i) 128 .convert(argumentTuple[i]); 129 } 130 } catch (IllegalActionException ex) { 131 throw new IllegalActionException(null, ex, 132 Token.notSupportedConversionMessage(token, "int")); 133 } 134 } 135 136 return new TupleToken(resultArray); 137 } 138 139 /** Determine if the argument represents the same TupleType as 140 * this object. Two function types are equal if they have the same 141 * field names and the type of each field is the same, and they 142 * have the same return type. 143 * @param object Another object. 144 * @return True if the argument represents the same TupleType as 145 * this object. 146 */ 147 @Override 148 public boolean equals(Object object) { 149 if (!(object instanceof TupleType)) { 150 return false; 151 } 152 153 TupleType TupleType = (TupleType) object; 154 155 if (getElementCount() != TupleType.getElementCount()) { 156 return false; 157 } 158 159 for (int i = 0; i < getElementCount(); i++) { 160 Type myType = this.getElementType(i); 161 Type argType = TupleType.getElementType(i); 162 163 if (!myType.equals(argType)) { 164 return false; 165 } 166 } 167 168 return true; 169 } 170 171 /** Return the number of arguments in this type. 172 * @return The number of arguments in this type. 173 */ 174 public int getElementCount() { 175 return _elementTypeTerms.length; 176 } 177 178 /** Return the type of the given argument. 179 * @param i The given argument. 180 * @return a Type. 181 */ 182 public Type getElementType(int i) { 183 if (i < 0 || i >= _elementTypeTerms.length) { 184 return null; 185 } 186 187 FieldTypeTerm fieldType = _elementTypeTerms[i]; 188 189 if (fieldType == null) { 190 return null; 191 } 192 193 return fieldType._resolvedType; 194 } 195 196 /** Return the class for tokens that this type represents. 197 * @return The class for tokens that this type represents. 198 */ 199 @Override 200 public Class getTokenClass() { 201 return FunctionToken.class; 202 } 203 204 /** Return the InequalityTerm representing the type of the given 205 * argument. 206 * @param i The given argument. 207 * @return An InequalityTerm. 208 * @see ptolemy.graph.InequalityTerm 209 */ 210 public FieldTypeTerm getArgTypeTerm(int i) { 211 return _elementTypeTerms[i]; 212 } 213 214 /** Return a hash code value for this object. 215 */ 216 @Override 217 public int hashCode() { 218 return Arrays.hashCode(_elementTypeTerms) + 2917; 219 } 220 221 /** Set the elements that have declared type BaseType.UNKNOWN (the leaf 222 * type variable) to the specified type. 223 * @param type the type to set the leaf type variable to. 224 */ 225 @Override 226 public void initialize(Type type) { 227 try { 228 for (int i = 0; i < getElementCount(); i++) { 229 FieldTypeTerm fieldType = getArgTypeTerm(i); 230 231 if (fieldType.isSettable()) { 232 fieldType.initialize(type); 233 } 234 } 235 } catch (IllegalActionException iae) { 236 throw new InternalErrorException("TupleType.initialize: Cannot " 237 + "initialize the element type to " + type + " " 238 + iae.getMessage()); 239 } 240 } 241 242 /** Test if the argument type is compatible with this type. The 243 * given type will be compatible with this type if it is 244 * BaseType.UNKNOWN, or... 245 * @param type An instance of Type. 246 * @return True if the argument is compatible with this type. 247 */ 248 @Override 249 public boolean isCompatible(Type type) { 250 if (type.equals(BaseType.UNKNOWN)) { 251 return true; 252 } 253 254 if (!(type instanceof TupleType)) { 255 return false; 256 } 257 258 TupleType argumentTupleType = (TupleType) type; 259 260 // The given type cannot be losslessly converted to this type 261 // if it does not contain the same number of arguments. 262 if (argumentTupleType.getElementCount() != getElementCount()) { 263 return false; 264 } 265 266 // Loop through all of the fields of this type... 267 for (int i = 0; i < getElementCount(); i++) { 268 Type argumentFieldTypeTerm = argumentTupleType.getElementType(i); 269 270 // The given function type cannot be losslessly converted 271 // to this type if the individual arguments are not 272 // compatible. 273 Type thisFieldTypeTerm = getElementType(i); 274 275 if (!argumentFieldTypeTerm.isCompatible(thisFieldTypeTerm)) { 276 return false; 277 } 278 } 279 280 return true; 281 } 282 283 /** Test if this TupleType is a constant. A TupleType is a 284 * constant if the declared type of all of its fields are 285 * constant. 286 * @return True if this type is a constant. 287 */ 288 @Override 289 public boolean isConstant() { 290 // Loop through all of the fields of this type... 291 for (int i = 0; i < getElementCount(); i++) { 292 FieldTypeTerm fieldType = getArgTypeTerm(i); 293 Type type = fieldType._declaredType; 294 295 // Return false if the field is not constant. 296 if (!type.isConstant()) { 297 return false; 298 } 299 } 300 301 return true; 302 } 303 304 /** Test if this type corresponds to an instantiable token 305 * class. A TupleType is instantiable if all of its fields are 306 * instantiable. 307 * @return True if this type is instantiable. 308 */ 309 @Override 310 public boolean isInstantiable() { 311 // Loop through all of the fields of this type... 312 for (int i = 0; i < getElementCount(); i++) { 313 Type type = getElementType(i); 314 315 // Return false if the field is not instantiable. 316 if (!type.isInstantiable()) { 317 return false; 318 } 319 } 320 321 return true; 322 } 323 324 /** Test if the specified type is a substitution instance of this 325 * type. One function is a substitution instance of another if they 326 * have arguments with the same types and each field of the given type is 327 * a substitution instance of the corresponding field in this type. 328 * @param type A Type. 329 * @return True if the argument is a substitution instance of this type. 330 * @see Type#isSubstitutionInstance 331 */ 332 @Override 333 public boolean isSubstitutionInstance(Type type) { 334 if (!(type instanceof TupleType)) { 335 return false; 336 } 337 338 TupleType TupleType = (TupleType) type; 339 340 // Check that the argument counts are the same 341 int argCount = getElementCount(); 342 343 if (TupleType.getElementCount() != argCount) { 344 return false; 345 } 346 347 // Loop through all of the fields of this type... 348 for (int i = 0; i < getElementCount(); i++) { 349 Type myArgType = getElementType(i); 350 Type argType = TupleType.getElementType(i); 351 352 if (!myArgType.isSubstitutionInstance(argType)) { 353 return false; 354 } 355 } 356 357 return true; 358 } 359 360 /** Return the string representation of this type. The format is 361 * function(a0:>type<, a1:>type<, ...) >type<. 362 * Note that the function argument names are not semantically 363 * significant. 364 * @return A String. 365 */ 366 @Override 367 public String toString() { 368 // construct the string representation of this token. 369 StringBuffer s = new StringBuffer("{"); 370 371 for (int i = 0; i < getElementCount(); i++) { 372 if (i != 0) { 373 s.append(", "); 374 } 375 376 s.append("a" + i + ":" + getElementType(i)); 377 } 378 379 return s.toString() + "}"; 380 } 381 382 /** Update this type to the specified TupleType. 383 * The specified type must be a TupleType and have the same structure 384 * as this one. 385 * This method will only update the component whose declared type is 386 * BaseType.UNKNOWN, and leave the constant part of this type intact. 387 * @param newType A StructuredType. 388 * @exception IllegalActionException If the specified type is not a 389 * TupleType or it does not have the same structure as this one. 390 */ 391 @Override 392 public void updateType(StructuredType newType) 393 throws IllegalActionException { 394 if (this.isConstant()) { 395 if (this.equals(newType)) { 396 return; 397 } else { 398 throw new IllegalActionException("TupleType.updateType: " 399 + "This type is a constant and the argument is not the" 400 + " same as this type. This type: " + this.toString() 401 + " argument: " + newType.toString()); 402 } 403 } 404 405 // This type is a variable. 406 if (!this.isSubstitutionInstance(newType)) { 407 throw new IllegalActionException("TupleType.updateType: " 408 + "Cannot update this type to the new type."); 409 } 410 411 // Loop through all of the fields of this type... 412 for (int i = 0; i < getElementCount(); i++) { 413 FieldTypeTerm argTypeTerm = getArgTypeTerm(i); 414 415 if (argTypeTerm.isSettable()) { 416 Type newArgType = ((TupleType) newType).getElementType(i); 417 argTypeTerm.setValue(newArgType); 418 } 419 } 420 } 421 422 /////////////////////////////////////////////////////////////////// 423 //// protected methods //// 424 425 /** Compare this type with the specified type. The specified type 426 * must be a TupleType, otherwise an exception will be thrown. 427 * 428 * This method returns one of ptolemy.graph.CPO.LOWER, 429 * ptolemy.graph.CPO.SAME, ptolemy.graph.CPO.HIGHER, 430 * ptolemy.graph.CPO.INCOMPARABLE, indicating this type is lower 431 * than, equal to, higher than, or incomparable with the 432 * specified type in the type hierarchy, respectively. 433 * @param type a TupleType. 434 * @return An integer. 435 * @exception IllegalArgumentException If the specified type is 436 * not a TupleType. 437 */ 438 @Override 439 protected int _compare(StructuredType type) { 440 if (!(type instanceof TupleType)) { 441 throw new IllegalArgumentException( 442 "TupleType.compare: " + "The argument is not a TupleType."); 443 } 444 445 if (this.equals(type)) { 446 return CPO.SAME; 447 } 448 449 if (_isLessThanOrEqualTo(this, (TupleType) type)) { 450 return CPO.LOWER; 451 } 452 453 if (_isLessThanOrEqualTo((TupleType) type, this)) { 454 return CPO.HIGHER; 455 } 456 457 return CPO.INCOMPARABLE; 458 } 459 460 /** Return a static instance of TupleType. 461 * @return a TupleType. 462 */ 463 @Override 464 protected StructuredType _getRepresentative() { 465 return _representative; 466 } 467 468 /** Return the greatest lower bound of this type with the specified 469 * type. The specified type must be a TupleType, otherwise an 470 * exception will be thrown. 471 * @param type a TupleType. 472 * @return a TupleType. 473 * @exception IllegalArgumentException If the specified type is 474 * not a TupleType. 475 */ 476 @Override 477 protected StructuredType _greatestLowerBound(StructuredType type) { 478 if (!(type instanceof TupleType)) { 479 throw new IllegalArgumentException( 480 "TupleType.greatestLowerBound: The argument is not a " 481 + "TupleType."); 482 } 483 484 TupleType TupleType = (TupleType) type; 485 486 // construct the GLB FunctionToken 487 int argCount = getElementCount(); 488 489 if (TupleType.getElementCount() != argCount) { 490 throw new IllegalArgumentException( 491 "Types are not comparable because they have" 492 + " different numbers of arguments"); 493 } 494 495 Type[] types = new Type[argCount]; 496 497 for (int i = 0; i < argCount; i++) { 498 Type type1 = getElementType(i); 499 Type type2 = TupleType.getElementType(i); 500 501 if (type1 == null) { 502 types[i] = type2; 503 } else if (type2 == null) { 504 types[i] = type1; 505 } else { 506 types[i] = (Type) TypeLattice.lattice() 507 .greatestLowerBound(type1, type2); 508 } 509 } 510 511 return new TupleType(types); 512 } 513 514 /** Return the least upper bound of this type with the specified 515 * type. The specified type must be a TupleType, otherwise an 516 * exception will be thrown. 517 * @param type a TupleType. 518 * @return a TupleType. 519 * @exception IllegalArgumentException If the specified type is 520 * not a TupleType. 521 */ 522 @Override 523 protected StructuredType _leastUpperBound(StructuredType type) { 524 if (!(type instanceof TupleType)) { 525 throw new IllegalArgumentException("TupleType.leastUpperBound: " 526 + "The argument is not a TupleType."); 527 } 528 529 TupleType TupleType = (TupleType) type; 530 531 // construct the LUB FunctionToken 532 int argCount = getElementCount(); 533 534 if (TupleType.getElementCount() != argCount) { 535 throw new IllegalArgumentException( 536 "Types are not comparable because they have" 537 + " different numbers of arguments"); 538 } 539 540 Type[] types = new Type[argCount]; 541 542 for (int i = 0; i < argCount; i++) { 543 Type type1 = getElementType(i); 544 Type type2 = TupleType.getElementType(i); 545 546 if (type1 == null) { 547 types[i] = type2; 548 } else if (type2 == null) { 549 types[i] = type1; 550 } else { 551 types[i] = (Type) TypeLattice.lattice().leastUpperBound(type1, 552 type2); 553 } 554 } 555 556 return new TupleType(types); 557 } 558 559 /////////////////////////////////////////////////////////////////// 560 //// private methods //// 561 // Test if the first TupleType is less than or equal to the second 562 private boolean _isLessThanOrEqualTo(TupleType t1, TupleType t2) { 563 // construct the LUB FunctionToken 564 int argCount = t1.getElementCount(); 565 566 if (t2.getElementCount() != argCount) { 567 return false; 568 } 569 570 // iterate over the labels of the second type 571 for (int i = 0; i < argCount; i++) { 572 Type type1 = t1.getElementType(i); 573 Type type2 = t2.getElementType(i); 574 int result = TypeLattice.compare(type1, type2); 575 576 if (result == CPO.HIGHER || result == CPO.INCOMPARABLE) { 577 return false; 578 } 579 } 580 581 return true; 582 } 583 584 /////////////////////////////////////////////////////////////////// 585 //// private variables //// 586 // Mapping from label to field information. 587 private FieldTypeTerm[] _elementTypeTerms; 588 589 // the representative in the type lattice is the empty function. 590 private static TupleType _representative = new TupleType(new Type[0]); 591 592 /////////////////////////////////////////////////////////////////// 593 //// inner class //// 594 // A class that encapsulates the declared and resolved types of a 595 // field and implements the InequalityTerm interface. 596 private class FieldTypeTerm implements InequalityTerm { 597 // Construct an instance of FieldTypeTerm. 598 private FieldTypeTerm(Type declaredType) { 599 try { 600 _declaredType = (Type) declaredType.clone(); 601 _resolvedType = _declaredType; 602 } catch (CloneNotSupportedException cnse) { 603 throw new InternalErrorException("TupleType.FieldTypeTerm: " 604 + "The specified type cannot be cloned."); 605 } 606 } 607 608 /////////////////////////////////////////////////////////////// 609 //// public inner methods //// 610 611 /** Return this TupleType. 612 * @return a TupleType. 613 */ 614 @Override 615 public Object getAssociatedObject() { 616 return TupleType.this; 617 } 618 619 /** Return the resolved type. 620 * @return a Type. 621 */ 622 @Override 623 public Object getValue() { 624 return _resolvedType; 625 } 626 627 /** Return this FieldTypeTerm in an array if it represents a type 628 * variable. Otherwise, return an array of size zero. 629 * @return An array of InequalityTerm. 630 */ 631 @Override 632 public InequalityTerm[] getVariables() { 633 if (isSettable()) { 634 InequalityTerm[] variable = new InequalityTerm[1]; 635 variable[0] = this; 636 return variable; 637 } 638 639 return new InequalityTerm[0]; 640 } 641 642 /** Reset the variable part of the element type to the specified 643 * type. 644 * @param e A Type. 645 * @exception IllegalActionException If this type is not settable, 646 * or the argument is not a Type. 647 */ 648 @Override 649 public void initialize(Object e) throws IllegalActionException { 650 if (!isSettable()) { 651 throw new IllegalActionException("TupleType$FieldTypeTerm." 652 + "initialize: The type is not settable."); 653 } 654 655 if (!(e instanceof Type)) { 656 throw new IllegalActionException("FieldTypeTerm.initialize: " 657 + "The argument is not a Type."); 658 } 659 660 if (_declaredType == BaseType.UNKNOWN) { 661 _resolvedType = (Type) e; 662 } else { 663 // this field type is a structured type. 664 ((StructuredType) _resolvedType).initialize((Type) e); 665 } 666 } 667 668 /** Test if this field type is a type variable. 669 * @return True if this field type is a type variable. 670 */ 671 @Override 672 public boolean isSettable() { 673 return !_declaredType.isConstant(); 674 } 675 676 /** Check whether the current element type is acceptable. 677 * The element type is acceptable if it represents an 678 * instantiable object. 679 * @return True if the element type is acceptable. 680 */ 681 @Override 682 public boolean isValueAcceptable() { 683 return _resolvedType.isInstantiable(); 684 } 685 686 /** Set the element type to the specified type. 687 * @param e a Type. 688 * @exception IllegalActionException If the specified type violates 689 * the declared field type. 690 */ 691 @Override 692 public void setValue(Object e) throws IllegalActionException { 693 if (!isSettable()) { 694 throw new IllegalActionException( 695 "TupleType$FieldTypeTerm.setValue: The type is not " 696 + "settable."); 697 } 698 699 if (!_declaredType.isSubstitutionInstance((Type) e)) { 700 throw new IllegalActionException("FieldTypeTerm.setValue: " 701 + "Cannot update the field type of this TupleType " 702 + "to the new type." + " Field type: " 703 + _declaredType.toString() + ", New type: " 704 + e.toString()); 705 } 706 707 if (_declaredType == BaseType.UNKNOWN) { 708 try { 709 _resolvedType = (Type) ((Type) e).clone(); 710 } catch (CloneNotSupportedException cnse) { 711 throw new InternalErrorException( 712 "TupleType$FieldTypeTerm.setValue: " 713 + "The specified type cannot be cloned."); 714 } 715 } else { 716 ((StructuredType) _resolvedType).updateType((StructuredType) e); 717 } 718 } 719 720 /** Return a string representation of this term. 721 * @return A String. 722 */ 723 @Override 724 public String toString() { 725 return "(FunctionFieldTypeTerm, " + getValue() + ")"; 726 } 727 728 /////////////////////////////////////////////////////////////// 729 //// private inner variables //// 730 private Type _declaredType = null; 731 732 private Type _resolvedType = null; 733 } 734}