001/** A class representing the type of an ArrayToken.
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
027 */
028package ptolemy.data.type;
029
030import ptolemy.data.ArrayToken;
031import ptolemy.data.Token;
032import ptolemy.graph.InequalityTerm;
033import ptolemy.kernel.util.IllegalActionException;
034import ptolemy.kernel.util.InternalErrorException;
035
036/**
037
038 A class representing the type of an ArrayToken.
039
040 @author Steve Neuendorffer, Yuhong Xiong
041 @version $Id$
042 @since Ptolemy II 4.0
043 @Pt.ProposedRating Red (cxh)
044 @Pt.AcceptedRating Red (cxh)
045 */
046public class ArrayType extends StructuredType implements Cloneable {
047    /** Construct a new ArrayType with the specified type for the array
048     *  elements. To leave the element type undeclared, use BaseType.UNKNOWN.
049     *  @param elementType The type of the array elements.
050     *  @exception IllegalArgumentException If the argument is null.
051     */
052    public ArrayType(Type elementType) {
053        if (elementType == null) {
054            throw new IllegalArgumentException(
055                    "Cannot create ArrayType " + " with null elementType");
056        }
057
058        try {
059            _declaredElementType = (Type) elementType.clone();
060        } catch (CloneNotSupportedException cnse) {
061            throw new InternalErrorException(
062                    "The specified type " + elementType + " cannot be cloned.");
063        }
064
065        _elementType = _declaredElementType;
066        _length = -1;
067    }
068
069    /** Construct a new ArrayType with the specified type for the array
070     *  elements. To leave the element type undeclared, use BaseType.UNKNOWN.
071     *  @param elementType The type of the array elements.
072     *  @param length Then length of the array.
073     *  @exception IllegalArgumentException If the argument is null.
074     */
075    public ArrayType(Type elementType, int length) {
076        this(elementType);
077        if (length < 0) {
078            throw new IllegalArgumentException(
079                    "Cannot create ArrayType " + "with negative length.");
080        }
081        _length = length;
082    }
083
084    ///////////////////////////////////////////////////////////////////
085    ////                         public methods                    ////
086
087    /** Return a type constraint that can be used to constrain
088     *  another typeable object to have a type related to an
089     *  array whose element type is the type of the specified
090     *  typeable.  A typical usage of this is as follows:
091     *  <pre>
092     *      output.setTypeAtLeast(ArrayType.arrayOf(input));
093     *  </pre>
094     *  where input and output are ports (this is the type
095     *  constraint of SequenceToArray, for example).
096     *  @param typeable A typeable.
097     *  @return An InequalityTerm that can be passed to methods
098     *   like setTypeAtLeast() of the Typeable interface.
099     *  @exception IllegalActionException If the specified typeable
100     *   cannot be set to an array type.
101     */
102    public static InequalityTerm arrayOf(Typeable typeable)
103            throws IllegalActionException {
104        return new TypeableArrayTypeTerm(typeable);
105    }
106
107    /** Return a type constraint that can be used to constrain
108     *  another typeable object to have a type related to an
109     *  array whose element type is the type of the specified
110     *  typeable.  A typical usage of this is as follows:
111     *  <pre>
112     *      output.setTypeAtLeast(ArrayType.arrayOf(input, length));
113     *  </pre>
114     *  where input and output are ports (this is the type
115     *  constraint of SequenceToArray, for example).
116     *  @param typeable A typeable.
117     *  @param length The length of array.
118     *  @return An InequalityTerm that can be passed to methods
119     *   like setTypeAtLeast() of the Typeable interface.
120     *  @exception IllegalActionException If the specified typeable
121     *   cannot be set to an array type.
122     */
123    public static InequalityTerm arrayOf(Typeable typeable, int length)
124            throws IllegalActionException {
125        return new TypeableSizedArrayTypeTerm(typeable, length);
126    }
127
128    /** Return a deep copy of this ArrayType if it is a variable, or
129     *  itself if it is a constant.
130     *  @return An ArrayType.
131     */
132    @Override
133    public Object clone() {
134        ArrayType newObj = new ArrayType(_declaredElementType);
135        newObj._length = _length;
136
137        try {
138            newObj.updateType(this);
139        } catch (IllegalActionException ex) {
140            throw new InternalErrorException("ArrayType.clone: "
141                    + "Cannot update new instance. " + ex.getMessage());
142        }
143        return newObj;
144    }
145
146    /** Convert the argument token into an ArrayToken having this
147     *  type, if lossless conversion can be done.  If the argument
148     *  is not an ArrayToken, then the result is an array token with
149     *  one entry, the argument.
150     *  @param token A token.
151     *  @return An ArrayToken.
152     *  @exception IllegalActionException If lossless conversion
153     *   cannot be done.
154     */
155    @Override
156    public Token convert(Token token) throws IllegalActionException {
157        Type myElementType = getElementType();
158        // Cannot convert to unknown element type.
159        if (myElementType.equals(BaseType.UNKNOWN)) {
160            if (token instanceof ArrayToken) {
161                // Following the logic implemented in BaseType for UNKNOWN,
162                // since every array token is a substitution instance for
163                // {unknown}, just return the token.
164                return token;
165            }
166            // If it's not an ArrayToken, then something is wrong.
167            throw new IllegalActionException(
168                    "Cannot convert " + token + " to type {unknown}");
169        }
170        if (!(token instanceof ArrayToken)) {
171            if (hasKnownLength() && length() != 1) {
172                throw new IllegalActionException(null,
173                        Token.notSupportedConversionMessage(token, toString()));
174            }
175            // NOTE: Added 7/17/06 by EAL to support type -> {type} conversion.
176            Token[] contents = new Token[1];
177            contents[0] = token;
178            return new ArrayToken(myElementType, contents);
179        }
180
181        ArrayToken argumentArrayToken = (ArrayToken) token;
182        if (hasKnownLength() && argumentArrayToken.length() != length()) {
183            throw new IllegalActionException(null,
184                    Token.notSupportedConversionMessage(token, toString()));
185        }
186
187        if (myElementType.equals(argumentArrayToken.getElementType())) {
188            return token;
189        }
190
191        Token[] argumentArray = argumentArrayToken.arrayValue();
192        Token[] resultArray = new Token[argumentArray.length];
193
194        try {
195            for (int i = 0; i < argumentArray.length; i++) {
196                resultArray[i] = myElementType.convert(argumentArray[i]);
197            }
198        } catch (IllegalActionException ex) {
199            throw new IllegalActionException(null, ex,
200                    Token.notSupportedConversionMessage(token, "int"));
201        }
202
203        if (resultArray.length < 1) {
204            // Support your local zero length array.
205            // actor/lib/test/auto/NilTokenTypeTest.xml requires this.
206            Type argumentArrayElementType = argumentArrayToken.getElementType();
207            try {
208                return new ArrayToken(argumentArrayElementType);
209            } catch (Exception ex) {
210                throw new IllegalActionException(null, ex,
211                        "Failed to construct an array of type "
212                                + argumentArrayElementType);
213            }
214        }
215        return new ArrayToken(myElementType, resultArray);
216    }
217
218    /** Return the depth of an array type. The depth of an
219     *  array type is the number of times it
220     *  contains other structured types. For example, an array
221     *  of arrays has depth 2, and an array of arrays of records
222     *  has depth 3.
223     *  @return the depth of a structured type.
224     */
225    @Override
226    public int depth() {
227        int depth = 1;
228        if (_elementType instanceof StructuredType) {
229            depth += ((StructuredType) _elementType).depth();
230        }
231        return depth;
232    }
233
234    /** Determine if the argument represents the same ArrayType as this
235     *  object.
236     *  @param object Another object.
237     *  @return True if the argument represents the same ArrayType as
238     *   this object; false otherwise.
239     */
240    @Override
241    public boolean equals(Object object) {
242        if (!(object instanceof ArrayType)) {
243            return false;
244        }
245
246        ArrayType argumentType = (ArrayType) object;
247        return _elementType.equals(argumentType.getElementType())
248                && _length == argumentType._length;
249    }
250
251    /** Return a type constraint that can be used to constrain
252     *  another typeable object to have a type related to the
253     *  element type of the specified typeable.  As a side
254     *  effect, the specified typeable is constrained to have an array
255     *  type.  A typical usage of this is as follows:
256     *  <pre>
257     *      output.setTypeAtLeast(ArrayType.elementType(input));
258     *  </pre>
259     *  where input and output are ports. This forces the input
260     *  port to have an array type and the output port to have
261     *  a type at least that of the elements of input arrays.
262     *  @param typeable An array-valued typeable.
263     *  @return An InequalityTerm that can be passed to methods
264     *   like setTypeAtLeast() of the Typeable interface.
265     *  @exception IllegalActionException If the specified typeable
266     *   cannot be set to an array type.
267     */
268    public static InequalityTerm elementType(Typeable typeable)
269            throws IllegalActionException {
270        typeable.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
271        return new TypeableElementTypeTerm(typeable);
272    }
273
274    /** Return the declared type of the array elements.
275     *  @return a Type.
276     */
277    public Type getDeclaredElementType() {
278        return _declaredElementType;
279    }
280
281    /** Return the type of the array elements.
282     *  @return a Type.
283     */
284    public Type getElementType() {
285        return _elementType;
286    }
287
288    /** Return the InequalityTerm representing the element type.
289     *  @return An InequalityTerm.
290     *  @see ptolemy.graph.InequalityTerm
291     */
292    public InequalityTerm getElementTypeTerm() {
293        // This should be public because of copernicus.java.TypeSpecializer
294        return _elemTypeTerm;
295    }
296
297    /** Return the class for tokens that this type represents.
298     *  @return The class for tokens that this type represents.
299     */
300    @Override
301    public Class getTokenClass() {
302        return ArrayToken.class;
303    }
304
305    /** Return a hash code value for this object.
306     */
307    @Override
308    public int hashCode() {
309        return _elementType.hashCode() + 2917;
310    }
311
312    /** Return true if the length of this array type has been determined.
313     *  @return true if the length has been determined.
314     */
315    public boolean hasKnownLength() {
316        return _length >= 0;
317    }
318
319    /** Return true if this type does not correspond to a single token
320     *  class.  This occurs if the type is not instantiable, or it
321     *  represents either an abstract base class or an interface.
322     *  @return true if the element type is abstract.
323     */
324    @Override
325    public boolean isAbstract() {
326        return _elementType.isAbstract() || !hasKnownLength();
327    }
328
329    /** Set the elements that have declared type BaseType.UNKNOWN (the leaf
330     *  type variable) to the specified type.
331     *  @param t the type to set the leaf type variable to.
332     */
333    @Override
334    public void initialize(Type t) {
335        try {
336            if (!isConstant()) {
337                _elemTypeTerm.initialize(t);
338            }
339        } catch (IllegalActionException iae) {
340            throw new InternalErrorException("ArrayType.initialize: Cannot "
341                    + "initialize the element type to " + t + ". "
342                    + iae.getMessage());
343        }
344    }
345
346    /** Test if the argument type is compatible with this type.
347     *  If this type is a constant, the argument is compatible if it is less
348     *  than or equal to this type in the type lattice; If this type is a
349     *  variable, the argument is compatible if it is a substitution
350     *  instance of this type.
351     *  @param type A Type.
352     *  @return True if the argument is compatible with this type.
353     *  @see ptolemy.data.type.ArrayType#convert
354     */
355    @Override
356    public boolean isCompatible(Type type) {
357        ArrayType arrayType;
358
359        if (type instanceof ArrayType) {
360            arrayType = (ArrayType) type;
361            if (hasKnownLength() && arrayType.hasKnownLength()
362                    && length() != arrayType.length()) {
363                return false;
364            }
365            // If the length of this type is unknown, then the
366            // argument length is compatible.
367        } else {
368            if (type.equals(BaseType.GENERAL)) {
369                // If we have a Const {1,2,3} -> Display, then
370                // this method needs to return true because Display
371                // has an input port of type General.
372                return true;
373            }
374            return false;
375        }
376
377        Type elementType = arrayType.getElementType();
378        return _elementType.isCompatible(elementType);
379    }
380
381    /** Test if this ArrayType is a constant. An ArrayType is a constant if
382     *  it does not contain BaseType.UNKNOWN in any level.
383     *  @return True if this type is a constant.
384     */
385    @Override
386    public boolean isConstant() {
387        return _declaredElementType.isConstant();
388    }
389
390    /** Determine if this type corresponds to an instantiable token
391     *  class. An ArrayType is instantiable if its element type is
392     *  instantiable.
393     *  @return True if this type is instantiable.
394     */
395    @Override
396    public boolean isInstantiable() {
397        return _elementType.isInstantiable();
398    }
399
400    /** Return true if the specified type is a substitution instance of this
401     *  type.
402     *  @param type A Type.
403     *  @return True if the argument is a substitution instance of this type.
404     *  @see Type#isSubstitutionInstance
405     */
406    @Override
407    public boolean isSubstitutionInstance(Type type) {
408        ArrayType arrayType;
409        if (type instanceof ArrayType) {
410            arrayType = (ArrayType) type;
411            if (hasKnownLength() && arrayType.hasKnownLength()
412                    && length() != arrayType.length()) {
413                return false;
414            }
415        } else {
416            return false;
417        }
418
419        Type argElemType = ((ArrayType) type).getElementType();
420        return _declaredElementType.isSubstitutionInstance(argElemType);
421    }
422
423    /** Return the length of this array type.
424     *  @return The length of this type.
425     *  @exception RuntimeException If the length is not known.
426     */
427    public int length() {
428        if (!hasKnownLength()) {
429            throw new RuntimeException("Length is not known.");
430        }
431        return _length;
432    }
433
434    /** Set the type to the specified type, which is required to be
435     *  an array type.
436     *  @param type The new type.
437     *  @exception IllegalActionException If the specified type is not
438     *   an instance of ArrayType.
439     */
440    public void setType(Type type) throws IllegalActionException {
441        if (!(type instanceof ArrayType)) {
442            throw new IllegalActionException(
443                    "Cannot change an array type to a non-array type.");
444        }
445        try {
446            Type clone = (Type) ((ArrayType) type).getElementType().clone();
447            _elementType = clone;
448            _declaredElementType = clone;
449            _length = ((ArrayType) type).length();
450        } catch (CloneNotSupportedException e) {
451            throw new InternalErrorException(e);
452        }
453    }
454
455    /** Return the string representation of this type. The format is
456     *  {<i>type</i>}, where <i>type</i> is the element type.
457     *  @return A String.
458     */
459    @Override
460    public String toString() {
461        if (hasKnownLength()) {
462            return "arrayType(" + getElementType().toString() + "," + _length
463                    + ")";
464        } else {
465            return "arrayType(" + getElementType().toString() + ")";
466        }
467    }
468
469    /** Update this Type to the specified ArrayType.
470     *  The specified type must be an ArrayType with the same structure as
471     *  this type, and have depth less than the MAXDEPTHBOUND.
472     *  This method will only update the component whose declared type is
473     *  BaseType.UNKNOWN, and leave the constant part of this type intact.
474     *  @param newType A StructuredType.
475     *  @exception IllegalActionException If the specified type is not an
476     *   ArrayType or it does not have the same structure as this one.
477     */
478    @Override
479    public void updateType(StructuredType newType)
480            throws IllegalActionException {
481        super.updateType(newType);
482
483        // This type is a variable.
484        if (!this.isSubstitutionInstance(newType)) {
485            throw new IllegalActionException(
486                    "ArrayType.updateType: " + "The type " + this
487                            + " cannot be updated to " + newType + ".");
488        }
489
490        ArrayType arrayType = (ArrayType) newType;
491        if (!arrayType.hasKnownLength() || arrayType._length != _length) {
492            _length = -1; // Other length cases should be guarded by
493            // the isSubstituionInstance method
494        }
495
496        Type newElemType = ((ArrayType) newType).getElementType();
497
498        if (_declaredElementType.equals(BaseType.UNKNOWN)) {
499            try {
500                _elementType = (Type) newElemType.clone();
501            } catch (CloneNotSupportedException cnse) {
502                throw new InternalErrorException("ArrayType.updateType: "
503                        + "The specified element type cannot be cloned: "
504                        + _elementType);
505            }
506        } else if (!_declaredElementType.equals(newElemType)) {
507            // _declaredElementType is a StructuredType. _elementType
508            // must also be.
509            ((StructuredType) _elementType)
510                    .updateType((StructuredType) newElemType);
511        }
512    }
513
514    ///////////////////////////////////////////////////////////////////
515    ////                         public variables                  ////
516
517    /** A term to use when declaring the type of some parameter or port
518     *  to be an array.  The way to use this is to declare:
519     *  <pre>
520     *     param.setTypeAtLeast(ArrayType.ARRAY_BOTTOM);
521     *  </pre>
522     *  for a parameter "param".
523     */
524    public static final InequalityTerm ARRAY_BOTTOM = new ArrayBottomTypeTerm(
525            BaseType.ARRAY_BOTTOM);
526
527    /** A term to use when declaring the type of some parameter or port
528     *  to be an array, with unknown length.  The way to use this is to declare:
529     *  <pre>
530     *     param.setTypeAtLeast(ArrayType.ARRAY_UNSIZED_BOTTOM);
531     *  </pre>
532     *  for a parameter "param".
533     */
534    public static final InequalityTerm ARRAY_UNSIZED_BOTTOM = new ArrayBottomTypeTerm(
535            new ArrayType(BaseType.UNKNOWN));
536
537    //  (new ArrayType(BaseType.UNKNOWN) {
538    //         // This particular inequality term always has an acceptable type
539    //         // because it has no visible array that will ever be evaluated.
540    //         // It is essential that isValueAcceptable() return true, or the
541    //         // idiom above will result in reported type errors.
542    //         public InequalityTerm getElementTypeTerm() {
543    //             return _replacementElementTerm;
544    //         }
545
546    //         private InequalityTerm _replacementElementTerm = new ElementTypeTerm() {
547    //             public boolean isValueAcceptable() {
548    //                 return true;
549    //             }
550    //         };
551    //     }).getElementTypeTerm();
552
553    ///////////////////////////////////////////////////////////////////
554    ////                         protected methods                 ////
555
556    /** Compare this type with the specified type. The specified type
557     *  must be an ArrayType, otherwise an exception will be thrown.
558     *  This method returns one of ptolemy.graph.CPO.LOWER,
559     *  ptolemy.graph.CPO.SAME, ptolemy.graph.CPO.HIGHER,
560     *  ptolemy.graph.CPO.INCOMPARABLE, indicating this type is lower
561     *  than, equal to, higher than, or incomparable with the
562     *  specified type in the type hierarchy, respectively.
563     *  @param type an ArrayType.
564     *  @return An integer.
565     *  @exception IllegalArgumentException If the specified type is
566     *   not an ArrayType.
567     */
568    @Override
569    protected int _compare(StructuredType type) {
570        if (!(type instanceof ArrayType)) {
571            throw new IllegalArgumentException("ArrayType.compare: "
572                    + "The argument " + type + " is not an ArrayType.");
573        }
574
575        int retval = TypeLattice.compare(_elementType,
576                ((ArrayType) type).getElementType());
577
578        ArrayType arrayArgType = (ArrayType) type;
579        if (hasKnownLength() && arrayArgType.hasKnownLength()) {
580            if (length() != arrayArgType.length()) {
581                retval = ptolemy.graph.CPO.INCOMPARABLE;
582            }
583        } else if (hasKnownLength()) {
584            if (retval == ptolemy.graph.CPO.HIGHER) {
585                retval = ptolemy.graph.CPO.INCOMPARABLE;
586            } else if (retval == ptolemy.graph.CPO.SAME) {
587                // same element type but arrayArgType has no length [a, n] <= [a]
588                retval = ptolemy.graph.CPO.LOWER;
589            }
590        } else if (arrayArgType.hasKnownLength()) {
591            if (retval == ptolemy.graph.CPO.LOWER) {
592                retval = ptolemy.graph.CPO.INCOMPARABLE;
593            } else if (retval == ptolemy.graph.CPO.SAME) {
594                // same element type but this type has no length [a] >= [a, n]
595                retval = ptolemy.graph.CPO.HIGHER;
596            }
597        }
598
599        //System.out.println("comparing " + this + " and " + arrayArgType + " = " + retval);
600        return retval;
601    }
602
603    /** Return a static instance of ArrayType.
604     *  @return an ArrayType.
605     */
606    @Override
607    protected StructuredType _getRepresentative() {
608        return _representative;
609    }
610
611    /** Return the greatest lower bound of this type with the specified
612     *  type. The specified type must be an ArrayType, otherwise an
613     *  exception will be thrown.
614     *  @param type an ArrayType.
615     *  @return an ArrayType.
616     *  @exception IllegalArgumentException If the specified type is
617     *   not an ArrayType.
618     */
619    @Override
620    protected StructuredType _greatestLowerBound(StructuredType type) {
621        if (!(type instanceof ArrayType)) {
622            throw new IllegalArgumentException("ArrayType.greatestLowerBound: "
623                    + "The argument " + type + " is not an ArrayType.");
624        }
625
626        Type elementGLB = (Type) TypeLattice.lattice().greatestLowerBound(
627                _elementType, ((ArrayType) type).getElementType());
628
629        ArrayType arrayArgType = (ArrayType) type;
630        if (!hasKnownLength() && !arrayArgType.hasKnownLength()) {
631            return new ArrayType(elementGLB);
632        } else if (hasKnownLength() && arrayArgType.hasKnownLength()) {
633            if (length() != arrayArgType.length()) {
634                // FIXME:
635                // return BaseType.ARRAY_BOTTOM;
636            }
637        }
638        if (hasKnownLength()) {
639            return new ArrayType(elementGLB, length());
640        } else {
641            return new ArrayType(elementGLB, arrayArgType.length());
642        }
643    }
644
645    /** Return the least Upper bound of this type with the specified
646     *  type. The specified type must be an ArrayType, otherwise an
647     *  exception will be thrown.
648     *  @param type an ArrayType.
649     *  @return an ArrayType.
650     *  @exception IllegalArgumentException If the specified type is
651     *   not an ArrayType.
652     */
653    @Override
654    protected StructuredType _leastUpperBound(StructuredType type) {
655        if (!(type instanceof ArrayType)) {
656            throw new IllegalArgumentException("ArrayType.leastUpperBound: "
657                    + "The argument " + type + " is not an ArrayType.");
658        }
659
660        Type elementLUB = (Type) TypeLattice.lattice().leastUpperBound(
661                _elementType, ((ArrayType) type).getElementType());
662
663        ArrayType arrayArgType = (ArrayType) type;
664        if (hasKnownLength() && arrayArgType.hasKnownLength()) {
665            if (length() == arrayArgType.length()) {
666                return new ArrayType(elementLUB, length());
667            }
668        }
669
670        //   System.out.println("least upper bound of " + this + " and " + type + " = " + new ArrayType(elementLUB));
671
672        return new ArrayType(elementLUB);
673    }
674
675    ///////////////////////////////////////////////////////////////////
676    ////                         private variables                 ////
677    // the type of array elements.
678    private Type _declaredElementType;
679
680    private Type _elementType;
681
682    private int _length;
683
684    private ElementTypeTerm _elemTypeTerm = new ElementTypeTerm();
685
686    private static ArrayType _representative = new ArrayType(BaseType.UNKNOWN);
687
688    ///////////////////////////////////////////////////////////////////
689    ////                         inner classes                     ////
690
691    /** An InequalityTerm representing an arbitrary array type.
692     */
693    private static class ArrayBottomTypeTerm implements InequalityTerm {
694
695        /** Construct a term.
696         */
697        public ArrayBottomTypeTerm(Type type) {
698            _arrayType = type;
699        }
700
701        ///////////////////////////////////////////////////////////////
702        ////                   public inner methods                ////
703
704        /** Return an array type with element types given by the
705         *  associated typeable.
706         *  @return An ArrayType.
707         */
708        @Override
709        public Object getAssociatedObject() {
710            return _arrayType;
711        }
712
713        /** Return an array type with element types given by the
714         *  associated typeable.
715         *  @return An ArrayType.
716         *  @exception IllegalActionException If the type of the
717         *  associated typeable cannot be determined.
718         */
719        @Override
720        public Object getValue() throws IllegalActionException {
721            return _arrayType;
722        }
723
724        /** Return an array of size zero.
725         *  @return An array of InequalityTerm.
726         */
727        @Override
728        public InequalityTerm[] getVariables() {
729            return new InequalityTerm[0];
730        }
731
732        /** Throw an exception. This term cannot be set.
733         *  @param e A Type.
734         *  @exception IllegalActionException If this type is a constant,
735         *   or the argument is not a Type.
736         */
737        @Override
738        public void initialize(Object e) throws IllegalActionException {
739            throw new IllegalActionException(
740                    "ArrayType$ArraybottomTypeTerm.setValue: "
741                            + "Is not settable.");
742        }
743
744        /** Return false.
745         *  @return False.
746         */
747        @Override
748        public boolean isSettable() {
749            return false;
750        }
751
752        /** Delegate to an array type with elements given by the
753         *  type of the associated typeable.
754         *  @return True if the element type is acceptable.
755         */
756        @Override
757        public boolean isValueAcceptable() {
758            return true;
759        }
760
761        /** Throw an exception.
762         *  @param type a Type.
763         *  @exception IllegalActionException Always
764         */
765        @Override
766        public void setValue(Object type) throws IllegalActionException {
767            throw new IllegalActionException(
768                    "ArrayType$ArrayBottomTypeTerm.setValue: "
769                            + "Is not settable.");
770        }
771
772        /** Delegate to an array type with elements given by the
773         *  type of the associated typeable.
774         *  @return A String.
775         */
776        @Override
777        public String toString() {
778            return _arrayType.toString();
779        }
780
781        ///////////////////////////////////////////////////////////////
782        ////                   private members                     ////
783
784        /** The array type with element types matching the typeable. */
785        private Type _arrayType;
786    }
787
788    /** An InequalityTerm associated with an instance of ArrayType. */
789    private class ElementTypeTerm implements InequalityTerm {
790        ///////////////////////////////////////////////////////////////
791        ////                   public inner methods                ////
792
793        /** Return this ArrayType.
794         *  @return an ArrayType.
795         */
796        @Override
797        public Object getAssociatedObject() {
798            return ArrayType.this;
799        }
800
801        /** Return the element type.
802         *  @return a Type.
803         */
804        @Override
805        public Object getValue() {
806            return _elementType;
807        }
808
809        /** Return this ElementTypeTerm in an array if this term
810         *  represents a type variable. Otherwise, return an array of
811         *  size zero.
812         *  @return An array of InequalityTerm.
813         */
814        @Override
815        public InequalityTerm[] getVariables() {
816            if (isSettable()) {
817                InequalityTerm[] variable = new InequalityTerm[1];
818                variable[0] = this;
819                return variable;
820            }
821
822            return new InequalityTerm[0];
823        }
824
825        /** Reset the variable part of the element type to the specified
826         *  type.
827         *  @param e A Type.
828         *  @exception IllegalActionException If this type is a constant,
829         *   or the argument is not a Type.
830         */
831        @Override
832        public void initialize(Object e) throws IllegalActionException {
833            if (isConstant()) {
834                throw new IllegalActionException(
835                        "ArrayType$ElementTypeTerm.initialize: " + "This type "
836                                + this + " is not settable.");
837            }
838
839            if (!(e instanceof Type)) {
840                throw new IllegalActionException(
841                        "ArrayType$ElementTypeTerm.initialize: "
842                                + "The argument " + this + " is not a Type.");
843            }
844
845            if (_declaredElementType.equals(BaseType.UNKNOWN)) {
846                _elementType = (Type) e;
847            } else {
848                // element type is a structured type.
849                ((StructuredType) _elementType).initialize((Type) e);
850            }
851        }
852
853        /** Test if the element type is a type variable.
854         *  @return True if the element type is a type variable.
855         */
856        @Override
857        public boolean isSettable() {
858            return !_declaredElementType.isConstant();
859        }
860
861        /** Check whether the current element type is acceptable.
862         *  The element type is acceptable if it represents an
863         *  instantiable object.
864         *  @return True if the element type is acceptable.
865         */
866        @Override
867        public boolean isValueAcceptable() {
868            return _elementType.isInstantiable();
869        }
870
871        /** Set the element type to the specified type.
872         *  @param e a Type.
873         *  @exception IllegalActionException If the specified type violates
874         *   the declared type of the element.
875         */
876        @Override
877        public void setValue(Object e) throws IllegalActionException {
878            if (!isSettable()) {
879                throw new IllegalActionException(
880                        "ArrayType$ElementTypeTerm.setValue: This type " + e
881                                + " is not settable.");
882            }
883
884            if (!_declaredElementType.isSubstitutionInstance((Type) e)) {
885                // The LUB of the _elementType and another type is General,
886                // this is a type conflict.
887                throw new IllegalActionException(
888                        "ArrayType$ElementTypeTerm.setValue: "
889                                + "Cannot update the element type of this array to "
890                                + "the new type." + " Element type: "
891                                + _declaredElementType.toString()
892                                + ", New type: " + e.toString());
893            }
894
895            if (_declaredElementType.equals(BaseType.UNKNOWN)) {
896                try {
897                    _elementType = (Type) ((Type) e).clone();
898                } catch (CloneNotSupportedException cnse) {
899                    throw new InternalErrorException(
900                            "ArrayType$ElementTypeTerm.setValue: "
901                                    + "The specified type " + e
902                                    + " cannot be cloned.");
903                }
904            } else {
905                ((StructuredType) _elementType).updateType((StructuredType) e);
906            }
907        }
908
909        /** Return a string representation of this term.
910         *  @return A String.
911         */
912        @Override
913        public String toString() {
914            return "(ArrayElementType(" + getAssociatedObject() + "), "
915                    + getValue() + ")";
916        }
917    }
918
919    /** An InequalityTerm representing an array type whose elements
920     *  have the type of the specified typeable.  The purpose of this class
921     *  is to defer to as late as possible actually accessing
922     *  the type of the typeable, since it may change dynamically.
923     *  This term is not variable and cannot be set.
924     */
925    private static class TypeableArrayTypeTerm implements InequalityTerm {
926
927        /** Construct a term that will defer to the type of the
928         *  specified typeable.
929         *  @param typeable The object to defer requests to.
930         */
931        public TypeableArrayTypeTerm(Typeable typeable) {
932            _typeable = typeable;
933        }
934
935        ///////////////////////////////////////////////////////////////
936        ////                   public inner methods                ////
937
938        /** Return the associated typeable.
939         *  @return A Typeable.
940         */
941        @Override
942        public Object getAssociatedObject() {
943            return _typeable;
944        }
945
946        /** Return an array type with element types given by the associated typeable.
947         *  @return An ArrayType.
948         *  @exception IllegalActionException If the type of the associated typeable
949         *   cannot be determined.
950         */
951        @Override
952        public Object getValue() throws IllegalActionException {
953            return _getArrayTypeRaw();
954        }
955
956        /** Return an array of size zero.
957         *  @return An array of InequalityTerm.
958         */
959        @Override
960        public InequalityTerm[] getVariables() {
961            return new InequalityTerm[0];
962        }
963
964        /** Throw an exception. This term cannot be set.
965         *  @param e A Type.
966         *  @exception IllegalActionException If this type is a constant,
967         *   or the argument is not a Type.
968         */
969        @Override
970        public void initialize(Object e) throws IllegalActionException {
971            throw new IllegalActionException(
972                    "ArrayType$TypeableArrayTypeTerm.initialize: "
973                            + "This array type given with elements given by "
974                            + _typeable + " is not settable.");
975        }
976
977        /** Return false.
978         *  @return False.
979         */
980        @Override
981        public boolean isSettable() {
982            return false;
983        }
984
985        /** Delegate to an array type with elements given by the
986         *  type of the associated typeable.
987         *  @return True if the element type is acceptable.
988         */
989        @Override
990        public boolean isValueAcceptable() {
991            ArrayType type = _getArrayType();
992            return type.getElementTypeTerm().isValueAcceptable();
993        }
994
995        /** Throw an exception.
996         *  @param type a Type.
997         *  @exception IllegalActionException Always
998         */
999        @Override
1000        public void setValue(Object type) throws IllegalActionException {
1001            throw new IllegalActionException(
1002                    "ArrayType$TypeableArrayTypeTerm.setValue: "
1003                            + "The array type with element type given by "
1004                            + _typeable + " is not settable.");
1005        }
1006
1007        /** Delegate to an array type with elements given by the
1008         *  type of the associated typeable.
1009         *  @return A String.
1010         */
1011        @Override
1012        public String toString() {
1013            try {
1014                return "(TypeableArrayType(" + getAssociatedObject() + "), "
1015                        + getValue() + ")";
1016            } catch (IllegalActionException e) {
1017                throw new InternalErrorException(e);
1018            }
1019        }
1020
1021        ///////////////////////////////////////////////////////////////
1022        ////                   private methods                     ////
1023
1024        /** Get an array type with element type matching the type
1025         *  of the associated typeable.
1026         *  @return An array type for the associated typeable.
1027         */
1028        private ArrayType _getArrayType() {
1029            try {
1030                return _getArrayTypeRaw();
1031            } catch (IllegalActionException e) {
1032                throw new InternalErrorException(e);
1033            }
1034        }
1035
1036        /** Get an array type with element type matching the type
1037         *  of the associated typeable.
1038         *  @return An array type for the associated typeable.
1039         *  @exception IllegalActionException If the type of the typeable
1040         *   cannot be determined.
1041         */
1042        private ArrayType _getArrayTypeRaw() throws IllegalActionException {
1043            Type type = _typeable.getType();
1044            if (_arrayType == null
1045                    || !_arrayType.getElementType().equals(type)) {
1046                _arrayType = new ArrayType(type);
1047            }
1048            return _arrayType;
1049        }
1050
1051        ///////////////////////////////////////////////////////////////
1052        ////                   private members                     ////
1053
1054        /** The associated typeable. */
1055        private Typeable _typeable;
1056
1057        /** The array type with element types matching the typeable. */
1058        private ArrayType _arrayType;
1059    }
1060
1061    /** An InequalityTerm representing the element types
1062     *  of an instance of Typeable.  The purpose of this class
1063     *  is to defer to as late as possible actually accessing
1064     *  the type of the typeable, since it may change dynamically.
1065     */
1066    private static class TypeableElementTypeTerm implements InequalityTerm {
1067
1068        /** Construct a term that will defer to the type of the
1069         *  specified typeable.
1070         *  @param typeable The object to defer requests to.
1071         */
1072        public TypeableElementTypeTerm(Typeable typeable) {
1073            _typeable = typeable;
1074        }
1075
1076        ///////////////////////////////////////////////////////////////
1077        ////                   public inner methods                ////
1078
1079        /** Delegate to the element type term of the associated typeable.
1080         *  @return an ArrayType.
1081         */
1082        @Override
1083        public Object getAssociatedObject() {
1084            return _typeable;
1085        }
1086
1087        /** Delegate to the element type term of the associated typeable.
1088         *  @return a Type.
1089         *  @exception IllegalActionException If the delegate throws it.
1090         */
1091        @Override
1092        public Object getValue() throws IllegalActionException {
1093            InequalityTerm term = _getElementTypeTerm();
1094            if (term == null) {
1095                return BaseType.UNKNOWN;
1096            } else {
1097                return term.getValue();
1098            }
1099        }
1100
1101        /** Delegate to the element type term of the associated typeable.
1102         *  @return An array of InequalityTerm.
1103         */
1104        @Override
1105        public InequalityTerm[] getVariables() {
1106            if (isSettable()) {
1107                InequalityTerm[] variable = new InequalityTerm[1];
1108                variable[0] = this;
1109                return variable;
1110            }
1111
1112            return new InequalityTerm[0];
1113        }
1114
1115        /** Delegate to the element type term of the associated typeable.
1116         *  @param type A Type.
1117         *  @exception IllegalActionException If the delegate throws it.
1118         */
1119        @Override
1120        public void initialize(Object type) throws IllegalActionException {
1121            InequalityTerm term = _getElementTypeTerm();
1122            if (term == null) {
1123                return;
1124            } else {
1125                term.initialize(type);
1126            }
1127        }
1128
1129        /** Delegate to the element type term of the associated typeable.
1130         *  @return True if the element type is a type variable.
1131         */
1132        @Override
1133        public boolean isSettable() {
1134            InequalityTerm term = _getElementTypeTerm();
1135            if (term == null) {
1136                return true;
1137            } else {
1138                return term.isSettable();
1139            }
1140        }
1141
1142        /** Delegate to the element type term of the associated typeable.
1143         *  @return True if the element type is acceptable.
1144         */
1145        @Override
1146        public boolean isValueAcceptable() {
1147            InequalityTerm term = _getElementTypeTerm();
1148            if (term == null) {
1149                // Array has no element type.
1150                // If the type of the associated typable is
1151                // unknown and if this is acceptable, then it
1152                // is OK for the element types to be unknown.
1153                try {
1154                    Type arrayType = _typeable.getType();
1155                    if (BaseType.UNKNOWN.equals(arrayType)
1156                            && _typeable.isTypeAcceptable()) {
1157                        return true;
1158                    }
1159                } catch (IllegalActionException e) {
1160                    // Ignore and return false.
1161                }
1162                return false;
1163            } else {
1164                return term.isValueAcceptable();
1165            }
1166        }
1167
1168        /** Delegate to the element type term of the associated typeable.
1169         *  @param type a Type.
1170         *  @exception IllegalActionException If the specified type violates
1171         *   the declared type of the element.
1172         */
1173        @Override
1174        public void setValue(Object type) throws IllegalActionException {
1175            InequalityTerm term = _getElementTypeTerm();
1176            if (term == null) {
1177                return;
1178            } else {
1179                term.setValue(type);
1180            }
1181        }
1182
1183        /** Delegate to the element type term of the associated typeable.
1184         *  @return A String.
1185         */
1186        @Override
1187        public String toString() {
1188            try {
1189                return "(ArrayElementType(" + getAssociatedObject() + "), "
1190                        + getValue() + ")";
1191            } catch (IllegalActionException e) {
1192                throw new InternalErrorException(e);
1193            }
1194
1195        }
1196
1197        ///////////////////////////////////////////////////////////////
1198        ////                   private methods                     ////
1199
1200        /** Get an inequality term for elements of the associated
1201         *  typeable. If the associated typeable does not already have
1202         *  an array type, then return null, indicating that the type
1203         *  of the typeable hasn't yet resolved to an array type.
1204         *  @return An array type for the associated typeable.
1205         */
1206        private InequalityTerm _getElementTypeTerm() {
1207            try {
1208                Type type = _typeable.getType();
1209                if (!(type instanceof ArrayType)) {
1210                    return null;
1211                }
1212                return ((ArrayType) type).getElementTypeTerm();
1213            } catch (IllegalActionException e) {
1214                throw new InternalErrorException(e);
1215            }
1216        }
1217
1218        ///////////////////////////////////////////////////////////////
1219        ////                   private members                     ////
1220
1221        /** The associated typeable. */
1222        private Typeable _typeable;
1223    }
1224
1225    /** An InequalityTerm representing an array types whose elements
1226     *  have the type of the specified typeable.  The purpose of this class
1227     *  is to defer to as late as possible actually accessing
1228     *  the type of the typeable, since it may change dynamically.
1229     *  This term is not variable and cannot be set.
1230     */
1231    private static class TypeableSizedArrayTypeTerm implements InequalityTerm {
1232
1233        /** Construct a term that will defer to the type of the
1234         *  specified typeable.
1235         *  @param typeable The object to defer requests to.
1236         */
1237        public TypeableSizedArrayTypeTerm(Typeable typeable, int length) {
1238            _typeable = typeable;
1239            _length = length;
1240        }
1241
1242        ///////////////////////////////////////////////////////////////
1243        ////                   public inner methods                ////
1244
1245        /** Return an array type with element types given by the associated typeable.
1246         *  @return An ArrayType.
1247         */
1248        @Override
1249        public Object getAssociatedObject() {
1250            return _getArrayType();
1251        }
1252
1253        /** Return an array type with element types given by the associated typeable.
1254         *  @return An ArrayType.
1255         *  @exception IllegalActionException If the type of the associated typeable
1256         *   cannot be determined.
1257         */
1258        @Override
1259        public Object getValue() throws IllegalActionException {
1260            return _getArrayTypeRaw();
1261        }
1262
1263        /** Return an array of size zero.
1264         *  @return An array of InequalityTerm.
1265         */
1266        @Override
1267        public InequalityTerm[] getVariables() {
1268            return new InequalityTerm[0];
1269        }
1270
1271        /** Throw an exception. This term cannot be set.
1272         *  @param e A Type.
1273         *  @exception IllegalActionException If this type is a constant,
1274         *   or the argument is not a Type.
1275         */
1276        @Override
1277        public void initialize(Object e) throws IllegalActionException {
1278            throw new IllegalActionException(
1279                    "ArrayType$TypeableArrayTypeTerm.initialize: "
1280                            + "This array type given with elements given by "
1281                            + _typeable + " is not settable.");
1282        }
1283
1284        /** Return false.
1285         *  @return False.
1286         */
1287        @Override
1288        public boolean isSettable() {
1289            return false;
1290        }
1291
1292        /** Delegate to an array type with elements given by the
1293         *  type of the associated typeable.
1294         *  @return True if the element type is acceptable.
1295         */
1296        @Override
1297        public boolean isValueAcceptable() {
1298            ArrayType type = _getArrayType();
1299            return type.getElementTypeTerm().isValueAcceptable();
1300        }
1301
1302        /** Throw an exception.
1303         *  @param type a Type.
1304         *  @exception IllegalActionException Always
1305         */
1306        @Override
1307        public void setValue(Object type) throws IllegalActionException {
1308            throw new IllegalActionException(
1309                    "ArrayType$TypeableArrayTypeTerm.setValue: "
1310                            + "The array type with element type given by "
1311                            + _typeable + " is not settable.");
1312        }
1313
1314        /** Delegate to an array type with elements given by the
1315         *  type of the associated typeable.
1316         *  @return A String.
1317         */
1318        @Override
1319        public String toString() {
1320            return _getArrayType().toString();
1321        }
1322
1323        ///////////////////////////////////////////////////////////////
1324        ////                   private methods                     ////
1325
1326        /** Get an array type with element type matching the type
1327         *  of the associated typeable.
1328         *  @return An array type for the associated typeable.
1329         */
1330        private ArrayType _getArrayType() {
1331            try {
1332                return _getArrayTypeRaw();
1333            } catch (IllegalActionException e) {
1334                throw new InternalErrorException(e);
1335            }
1336        }
1337
1338        /** Get an array type with element type matching the type
1339         *  of the associated typeable.
1340         *  @return An array type for the associated typeable.
1341         *  @exception IllegalActionException If the type of the typeable
1342         *   cannot be determined.
1343         */
1344        private ArrayType _getArrayTypeRaw() throws IllegalActionException {
1345            Type type = _typeable.getType();
1346            if (_arrayType == null
1347                    || !_arrayType.getElementType().equals(type)) {
1348                _arrayType = new ArrayType(type, _length);
1349            }
1350            return _arrayType;
1351        }
1352
1353        ///////////////////////////////////////////////////////////////
1354        ////                   private members                     ////
1355
1356        /** The associated typeable. */
1357        private Typeable _typeable;
1358
1359        /** The array type with element types matching the typeable. */
1360        private ArrayType _arrayType;
1361
1362        private int _length;
1363    }
1364}