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:&gt;type&lt;, a1:&gt;type&lt;, ...) &gt;type&lt;.
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}