001: /* ====================================================================
002: * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
003: * ====================================================================
004: * The Tea Software License, Version 1.1
005: *
006: * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Walt Disney Internet Group (http://opensource.go.com/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact opensource@dig.com.
031: *
032: * 5. Products derived from this software may not be called "Tea",
033: * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
034: * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
035: * written permission of the Walt Disney Internet Group.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
041: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
042: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
043: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
044: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
045: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
046: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
047: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
048: * ====================================================================
049: *
050: * For more information about Tea, please see http://opensource.go.com/.
051: */
052:
053: package com.go.tea.parsetree;
054:
055: import java.util.LinkedList;
056: import java.util.Iterator;
057: import java.util.ListIterator;
058: import com.go.tea.compiler.SourceInfo;
059: import com.go.tea.compiler.Type;
060:
061: /******************************************************************************
062: * An Expression is a piece of code that, when executed, produces a value.
063: * All expressions have a type which represents the type of value is produces.
064: *
065: * @author Brian S O'Neill
066: * @version
067: * <!--$$Revision:--> 36 <!-- $-->, <!--$$JustDate:--> 01/01/25 <!-- $-->
068: */
069: public class Expression extends Node {
070: private LinkedList mConversions;
071: private boolean mPrimitive;
072: private boolean mExceptionPossible;
073:
074: public Expression(SourceInfo info) {
075: super (info);
076: mConversions = new LinkedList();
077: }
078:
079: public Object accept(NodeVisitor visitor) {
080: return visitor.visit(this );
081: }
082:
083: public Object clone() {
084: Expression e = (Expression) super .clone();
085: e.mConversions = (LinkedList) mConversions.clone();
086: return e;
087: }
088:
089: /**
090: * Returns true if an exception can be thrown while executing this
091: * Expression. By default, returns true only if a type conversion could
092: * cause an exception.
093: */
094: public boolean isExceptionPossible() {
095: return mExceptionPossible;
096: }
097:
098: /**
099: * The type of an expression is not necessarily set by a parser. It is
100: * typically set by a type checker. An expression's type may represent
101: * its natural type or its coerced type. A code generator is responsible
102: * for ensuring that the type it generates correctly matches the type
103: * of the expression.
104: *
105: * @return null if type is unknown
106: */
107: public Type getType() {
108: if (mConversions.isEmpty()) {
109: return null;
110: } else {
111: return ((Conversion) mConversions.getLast()).getToType();
112: }
113: }
114:
115: /**
116: * Returns the type of this expression before any conversions were applied,
117: * or null if the type isn't set.
118: */
119: public Type getInitialType() {
120: if (mConversions.isEmpty()) {
121: return null;
122: } else {
123: return ((Conversion) mConversions.getFirst()).getToType();
124: }
125: }
126:
127: /**
128: * Applies a type conversion to this expression which is chained to all
129: * previous conversions.
130: *
131: * @param toType the type to convert to.
132: */
133: public final void convertTo(Type toType) {
134: convertTo(toType, true);
135: }
136:
137: /**
138: * Applies a type conversion to this expression which is chained to all
139: * previous conversions.
140: *
141: * @param toType the type to convert to.
142: * @param preferCast a hint that the conversion should be performed by a
143: * type cast operation, by default is true.
144: * @throws IllegalArgumentException when the conversion is illegal.
145: */
146: public void convertTo(Type toType, boolean preferCast) {
147: Type fromType = getType();
148:
149: if (toType.equals(fromType)) {
150: return;
151: }
152:
153: boolean legal = false;
154:
155: if (!preferCast && fromType == Type.NULL_TYPE) {
156: preferCast = true;
157: }
158:
159: if (fromType == null) {
160: legal = true;
161: } else if (fromType.isPrimitive()) {
162: if (toType.isPrimitive()) {
163: if (toType.getNaturalClass() != void.class) {
164: legal = true;
165: }
166: } else {
167: Class fromObj = fromType.getObjectClass();
168: Class toObj = toType.getObjectClass();
169:
170: if (toObj.isAssignableFrom(fromObj)) {
171: legal = true;
172: if (fromObj != toObj) {
173: toType = fromType.toNonPrimitive();
174: }
175: } else if (Number.class.isAssignableFrom(fromObj)
176: && toType.hasPrimitivePeer()) {
177:
178: if (Number.class.isAssignableFrom(toObj)) {
179: legal = true;
180: convertTo(toType.toPrimitive());
181: } else if (Character.class.isAssignableFrom(toObj)) {
182: legal = true;
183: convertTo(new Type(char.class));
184: }
185: }
186: }
187: } else {
188: // From non-primitive...
189: if (toType.isPrimitive()) {
190: if (fromType.hasPrimitivePeer()) {
191: legal = true;
192: if (fromType.isNullable()) {
193: // NullPointerException is possible.
194: mExceptionPossible = true;
195: }
196:
197: Type fromPrim = fromType.toPrimitive();
198:
199: if (fromPrim.getNaturalClass() != toType
200: .getNaturalClass()) {
201:
202: convertTo(fromPrim);
203: }
204: } else {
205: Class fromObj = fromType.getObjectClass();
206: Class toObj = toType.getObjectClass();
207:
208: if (Number.class.isAssignableFrom(fromObj)
209: && Number.class.isAssignableFrom(toObj)) {
210: legal = true;
211: if (fromType.isNullable()) {
212: // NullPointerException is possible.
213: mExceptionPossible = true;
214: }
215: } else if (preferCast) {
216: legal = true;
217: convertTo(toType.toNonPrimitive(), true);
218: }
219: }
220: } else {
221: Class fromObj = fromType.getObjectClass();
222: Class toObj = toType.getObjectClass();
223:
224: if (fromObj.isAssignableFrom(toObj)) {
225: // Downcast.
226: if (preferCast) {
227: legal = true;
228: }
229: } else if (toObj.isAssignableFrom(fromObj)) {
230: // Upcast.
231: legal = true;
232: if (fromType.isNonNull() || !toType.isNonNull()) {
233: // No useful conversion applied, bail out.
234: return;
235: }
236: } else if (Number.class.isAssignableFrom(fromObj)
237: && Number.class.isAssignableFrom(toObj)
238: && toType.hasPrimitivePeer()) {
239: // Conversion like Integer -> Double.
240: legal = true;
241: if (fromType.isNonNull()) {
242: convertTo(toType.toPrimitive(), true);
243: }
244: }
245: // This test only captures array conversions.
246: else if (fromObj.getComponentType() != null
247: && toObj.getComponentType() != null
248: && toType.convertableFrom(fromType) >= 0) {
249: legal = true;
250: if (fromType.isNullable()) {
251: // NullPointerException is possible.
252: mExceptionPossible = true;
253: }
254: }
255: }
256: }
257:
258: if (!legal) {
259: // Try String conversion.
260: if (toType.getNaturalClass().isAssignableFrom(String.class)) {
261: legal = true;
262: if (toType.isNonNull()) {
263: addConversion(Type.NON_NULL_STRING_TYPE, false);
264: } else {
265: addConversion(Type.STRING_TYPE, false);
266: }
267: }
268: }
269:
270: if (!legal && !preferCast && !fromType.isPrimitive()
271: && !toType.isPrimitive()) {
272:
273: // Even though a cast isn't preferred, its the last available
274: // option.
275:
276: Class fromObj = fromType.getObjectClass();
277: Class toObj = toType.getObjectClass();
278:
279: if (fromObj.isAssignableFrom(toObj)) {
280: // Downcast.
281: legal = true;
282: } else if (toObj.isAssignableFrom(fromObj)) {
283: // Upcast.
284: legal = true;
285: }
286: }
287:
288: if (legal) {
289: addConversion(toType, preferCast);
290: } else {
291: throw new IllegalArgumentException("Can't convert "
292: + fromType + " to " + toType);
293: }
294: }
295:
296: private void addConversion(Type toType, boolean preferCast) {
297: Type fromType = getType();
298:
299: if (!toType.equals(fromType)) {
300: mConversions.add(new Conversion(fromType, toType,
301: preferCast));
302: }
303: }
304:
305: /**
306: * Returns a list of Conversion objects representing the all the
307: * conversions that have been applied to this Expression. Unless the type
308: * isn't set, the chain contains at least one element. The conversion
309: * chain may be reduced or expanded, so its length doesn't necessarily
310: * represent the exact sequence of calls to {@link #convertTo}.
311: */
312: public LinkedList getConversionChain() {
313: return reduce(mConversions);
314: }
315:
316: /**
317: * Sets the type of this expression, clearing the conversion chain.
318: */
319: public void setType(Type type) {
320: mConversions.clear();
321: mExceptionPossible = false;
322: if (type != null) {
323: // Prefer cast for initial type for correct operation of
324: // setInitialType if a conversion needs to be inserted at the
325: // beginning.
326: mConversions.add(new Conversion(null, type, true));
327: }
328: }
329:
330: /**
331: * Sets the intial type in the conversion chain, but does not clear the
332: * conversions.
333: */
334: public void setInitialType(Type type) {
335: Type initial = getInitialType();
336: if (type != null && !type.equals(initial)) {
337: if (initial == null) {
338: setType(type);
339: } else {
340: Iterator it = mConversions.iterator();
341: mConversions = new LinkedList();
342: // Prefer cast for initial type for correct operation of
343: // setInitialType if a conversion needs to be inserted at the
344: // beginning.
345: mConversions.add(new Conversion(null, type, true));
346: while (it.hasNext()) {
347: Conversion conv = (Conversion) it.next();
348: convertTo(conv.getToType(), conv.isCastPreferred());
349: }
350: }
351: }
352: }
353:
354: /**
355: * Returns true if the value generated by this expression is known at
356: * compile-time. For most expressions, false is returned. Literals
357: * always return true.
358: * @see Literal
359: */
360: public boolean isValueKnown() {
361: return false;
362: }
363:
364: /**
365: * Most expressions can't generate a value at compile-time, so this
366: * method simply returns null. Call isValueKnown to check if the
367: * expression's value is known at compile-time.
368: */
369: public Object getValue() {
370: return null;
371: }
372:
373: private LinkedList reduce(LinkedList conversions) {
374: outer: while (true) {
375: // Eliminate conversions that cancel each other out.
376:
377: ListIterator fromIterator = conversions.listIterator();
378: while (fromIterator.hasNext()) {
379: int fromIndex = fromIterator.nextIndex();
380: Type from = ((Conversion) fromIterator.next())
381: .getToType();
382:
383: ListIterator toIterator = conversions
384: .listIterator(fromIndex + 1);
385:
386: while (toIterator.hasNext()) {
387: int toIndex = toIterator.nextIndex();
388: Type to = ((Conversion) toIterator.next())
389: .getToType();
390: if (from.equals(to)) {
391: conversions.subList(fromIndex + 1, toIndex + 1)
392: .clear();
393: continue outer;
394: }
395: }
396: }
397:
398: // Reduce sequence where a primitive is converted to its
399: // non-primitive peer and then to a string. Eliminate the middle
400: // step and convert directly to a string.
401:
402: ListIterator it = conversions.listIterator();
403: while (it.hasNext()) {
404: Type type = ((Conversion) it.next()).getToType();
405: while (type.isPrimitive() && it.hasNext()) {
406: Type nextType = ((Conversion) it.next())
407: .getToType();
408: if (type.toNonPrimitive().equals(nextType)
409: && it.hasNext()) {
410:
411: Type thirdType = ((Conversion) it.next())
412: .getToType();
413: if (thirdType.getNaturalClass() == String.class) {
414: it.previous();
415: it.remove();
416: it.previous();
417: it.remove();
418: it.add(new Conversion(type, thirdType,
419: false));
420: } else {
421: type = thirdType;
422: }
423: } else {
424: type = nextType;
425: }
426: }
427: }
428:
429: break;
430: }
431:
432: return conversions;
433: }
434:
435: public static class Conversion {
436: private Type mFromType;
437: private Type mToType;
438: private boolean mPreferCast;
439:
440: Conversion(Type fromType, Type toType, boolean preferCast) {
441: mFromType = fromType;
442: if ((mToType = toType) == null) {
443: throw new NullPointerException("Cannot convert to null");
444: }
445: mPreferCast = preferCast;
446: }
447:
448: /**
449: * Is null if this is the first conversion in the chain.
450: */
451: public Type getFromType() {
452: return mFromType;
453: }
454:
455: public Type getToType() {
456: return mToType;
457: }
458:
459: public boolean isCastPreferred() {
460: return mPreferCast;
461: }
462:
463: public boolean equals(Object other) {
464: if (!(other instanceof Conversion)) {
465: return false;
466: }
467:
468: Conversion conv = (Conversion) other;
469:
470: if (mFromType == null) {
471: if (conv.mFromType != null) {
472: return false;
473: }
474: } else {
475: if (!mFromType.equals(conv.mFromType)) {
476: return false;
477: }
478: }
479:
480: return mToType.equals(conv.mToType)
481: && mPreferCast == conv.mPreferCast;
482: }
483:
484: public String toString() {
485: if (mFromType == null) {
486: return "Convert to " + mToType.getFullName();
487: } else {
488: return "Convert from " + mFromType.getFullName()
489: + " to " + mToType.getFullName();
490: }
491: }
492: }
493: }
|