001: /*=============================================================================
002: * Copyright Texas Instruments 2000. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.data;
022:
023: import java.util.*;
024: import java.lang.reflect.*;
025:
026: import oscript.util.*;
027: import oscript.exceptions.*;
028: import oscript.classwrap.ClassWrapGen;
029:
030: /**
031: * A <code>BuiltinType</code> instance is used to represent a built-in type.
032: * This is similar to <code>JavaClassWrapper</code>, in that it allows a java
033: * type to be sub-classed or instantiated, but it's difference is that it
034: * restricts access. Because the <code>Value</code> interface has dummy
035: * methods for all the possible methods it's subclass may implement, we have
036: * to do this to prevent the built-in types from appearing to have methods
037: * that they don't have. For example:
038: * <pre>
039: * var m = true.bopPlus;
040: * </pre>
041: * The <code>Value</code> dummy methods throw the appropriate exceptions
042: * if you try and use a non-existant method, for example:
043: * <pre>
044: * var v = true + false;
045: * </pre>
046: * To accomodate the possibility of things changing between when an object
047: * referencing this object is serialized, and when it is read back from a
048: * serial data stream (in a different invokation of the JVM), only the class-
049: * name is stored. The other paramters are loaded as via reflection as static
050: * fields of the named class:
051: * <ul>
052: * <li> public static String PARENT_TYPE_NAME; // class-name
053: * <li> public static String TYPE_NAME; // the name of this type
054: * <li> public static String[] MEMBER_NAMES; // names of members of this type
055: * </ul>
056: *
057: * @author Rob Clark (rob@ti.com)
058: * <!--$Format: " * @version $Revision$"$-->
059: * @version 1.38
060: */
061: public class BuiltinType extends JavaClassWrapper {
062: /**
063: * The name of the java class implementing this type. The other attributes
064: * of this object are determined using reflection, to ensure that they are
065: * up-to-date if this type is read from a serialized stream.
066: */
067: private String className;
068:
069: /**
070: * The name of this type.
071: */
072: transient private String typeName; // XXX we cant make an OString in constructor, because it needs OString.TYPE
073:
074: /**
075: * In the inheritance hierarchy, this type's parent.
076: */
077: transient private BuiltinType parentType;
078:
079: /**
080: * Build a table of our members, and parent type members, and so on,
081: * in order to quickly determine if we have a requested member.
082: */
083: transient private SymbolTable memberSet = null;
084:
085: /**
086: */
087: transient private boolean initialized = false;
088:
089: /**
090: * The constructor for <code>javaClass</code> that takes an array of
091: * <code>Value</code>. This is the constructor used by derived class
092: * when this built-in is subclassed.
093: */
094: transient private Constructor javaClassConstructor;
095: transient private Constructor javaWrapperClassConstructor;
096:
097: /**
098: * The parameter types to the <code>javaClassConstructor</code>
099: */
100: private static final Class[] PARAM_TYPES;
101:
102: static {
103: Class[] paramTypes = null;
104:
105: try {
106: paramTypes = new Class[] { Class
107: .forName("oscript.util.MemberTable") };
108: } catch (Throwable e) {
109: e.printStackTrace();
110: }
111:
112: PARAM_TYPES = paramTypes;
113: }
114:
115: private static final Class getJavaClass(String name) {
116: try {
117: return forName(name);
118: } catch (ClassNotFoundException e) {
119: e.printStackTrace();
120: return null;
121: }
122: }
123:
124: /**
125: * Table of all builtin types. A <code>BuiltinType</code> instance is
126: * intern'd, so there shouldn't be two instances representing the same
127: * builtin type.
128: */
129: private static Hashtable classCache;
130:
131: /**
132: * Get an instance of a built-in type. This method ensures that only a single
133: * instance representing a type gets created, regardless of the number of
134: * times this is called. This is needed to keep things sane when serialization
135: * is going on.
136: *
137: * @param className the name of the java class implementing the built-in type.
138: */
139: public static final BuiltinType makeBuiltinType(String className) {
140: return makeBuiltinType(className, null);
141: }
142:
143: private static synchronized final BuiltinType makeBuiltinType(
144: String className, BuiltinType type) {
145: if (classCache == null)
146: classCache = new Hashtable();
147:
148: BuiltinType bt = (BuiltinType) (classCache.get(className));
149:
150: if (bt == null) {
151: if (type != null)
152: bt = type;
153: else
154: bt = new BuiltinType(className);
155:
156: classCache.put(className, bt);
157: }
158:
159: return bt;
160: }
161:
162: /*=======================================================================*/
163: /**
164: * Class Constructor.
165: *
166: * @param className the name of the java class implementing the built-in type.
167: */
168: private BuiltinType(String className) {
169: super (getJavaClass(className));
170: this .className = className;
171: }
172:
173: /*=======================================================================*/
174: protected synchronized void init() {
175: if (!initialized) {
176: try {
177: if (javaClass.getField("PARENT_TYPE_NAME").get(null) != null)
178: parentType = makeBuiltinType((String) (javaClass
179: .getField("PARENT_TYPE_NAME").get(null)));
180: else
181: parentType = null;
182:
183: typeName = (String) (javaClass.getField("TYPE_NAME")
184: .get(null));
185:
186: memberSet = new OpenHashSymbolTable();
187: BuiltinType bt = this ;
188: while (bt != null) {
189: String[] names = (String[]) (bt.javaClass
190: .getField("MEMBER_NAMES").get(null));
191: for (int i = 0; i < names.length; i++)
192: memberSet.create(Symbol.getSymbol(names[i])
193: .getId());
194: bt = bt.parentType;
195: if ((bt != null) && (bt != this ))
196: bt.init();
197: }
198: } catch (Exception e) {
199: throw OJavaException.makeJavaExceptionWrapper(e);
200: }
201:
202: // JavaClassWrapper initializes itself only when needed, so we need
203: // to call init() before accessing any of the protected fields in
204: // our super-class:
205: super .init();
206: if (wrapperImpl != null)
207: wrapperImpl.init();
208:
209: try {
210: javaClassConstructor = impl.javaClass
211: .getConstructor(PARAM_TYPES);
212: if (wrapperImpl != null)
213: javaWrapperClassConstructor = wrapperImpl.javaClass
214: .getConstructor(PARAM_TYPES);
215: } catch (NoSuchMethodException e) {
216: // ignore... cannot be called as constructor
217: }
218:
219: initialized = true;
220: }
221: }
222:
223: /*=======================================================================*/
224: /**
225: * If this object is a type, determine if an instance of this type is
226: * an instance of the specified type, ie. if this is <code>type</code>,
227: * or a subclass.
228: *
229: * @param type the type to compare this type to
230: * @return <code>true</code> or <code>false</code>
231: * @throws PackagedScriptObjectException(NoSuchMemberException)
232: */
233: public boolean isA(Value type) {
234: // the check could be done only in init(), at expense of extra method call:
235: if (!initialized)
236: init();
237:
238: return (this == type.unhand())
239: || ((parentType != null) ? parentType.isA(type) : false);
240: }
241:
242: /*=======================================================================*/
243: /**
244: * Convert this object to a native java <code>String</code> value.
245: *
246: * @return a String value
247: * @throws PackagedScriptObjectException(NoSuchMethodException)
248: */
249: public String castToString() throws PackagedScriptObjectException {
250: // note: somehow due to eclipse's exception logging and error handling,
251: // we actually manage to get called recursively.. sort of.. anyways,
252: // and this seems to be the solution: (see #388)
253: if (typeName == null)
254: init();
255:
256: return typeName;
257: }
258:
259: /*=======================================================================*/
260: /**
261: * Perform the cast operation, <code>(a)b</code> is equivalent to <code>a.bopCast(b)</code>
262: *
263: * @param val the other value
264: * @return the result
265: * @throws PackagedScriptObjectException(NoSuchMemberException)
266: */
267: public Value bopCast(Value val)
268: throws PackagedScriptObjectException {
269: // note symbol is pushed to ScriptObject to work around a starup dependency issue
270: Value bopCast = getMember(ScriptObject._BOPCAST, false);
271: if (bopCast != null)
272: return bopCast.callAsFunction(new Value[] { val });
273: return super .bopCast(val);
274: }
275:
276: // bopCastR would be an instance member, not static (class) member
277:
278: /*=======================================================================*/
279: /**
280: * Overloaded to hide "non-existant" methods. (Ie. methods from Value
281: * base class.)
282: *
283: * @param obj an object of this type
284: * @param id the id of the symbol that maps to the member
285: * @return a reference to the member, or null
286: */
287: protected Value getTypeMember(Value obj, int id) {
288: // the check could be done only in init(), at expense of extra method call:
289: if (!initialized)
290: init();
291:
292: if (memberSet.get(id) != -1)
293: return super .getTypeMember(obj, id);
294: else if (obj.castToJavaObject() instanceof Proxy)
295: return ((Proxy) (obj.castToJavaObject())).getTypeMember(
296: obj, id);
297: else
298: return null;
299: }
300:
301: /*=======================================================================*/
302: /**
303: * Overloaded, because of how we handle args to the constructor.
304: */
305: protected Object doConstruct(StackFrame sf, MemberTable args,
306: boolean isWrapper) {
307: // the check could be done only in init(), at expense of extra method call:
308: if (!initialized)
309: init();
310:
311: Constructor c = isWrapper ? javaWrapperClassConstructor
312: : javaClassConstructor;
313:
314: if (c == null)
315: throw PackagedScriptObjectException
316: .makeExceptionWrapper(new OUnsupportedOperationException(
317: "can't call as constructor"));
318:
319: try {
320: if (args == null)
321: args = new OArray(0);
322: Object[] arr = new Object[] { args };
323: return (Value) (c.newInstance(arr));
324: } catch (InvocationTargetException e) {
325: Throwable t = e.getTargetException();
326:
327: if (t instanceof PackagedScriptObjectException)
328: throw (PackagedScriptObjectException) t;
329: else
330: throw PackagedScriptObjectException
331: .makeExceptionWrapper(new OException(t + ": "
332: + t.getMessage()));
333: } catch (Throwable e) {
334: if (DEBUG)
335: e.printStackTrace();
336: throw PackagedScriptObjectException
337: .makeExceptionWrapper(new OException(e + ": "
338: + e.getMessage()));
339: }
340: }
341:
342: /*=======================================================================*/
343: /**
344: * maintains unique-ness of a JavaClassWrapper when stuff gets serialized or
345: * un-serialized
346: */
347: Object readResolve() throws java.io.ObjectStreamException {
348: return makeBuiltinType(className, this );
349: }
350:
351: /*=======================================================================*/
352: /**
353: * Derived classes that implement {@link #getTypeMember} should also
354: * implement this.
355: *
356: * @param s the set to populate
357: * @param debugger <code>true</code> if being used by debugger, in
358: * which case both public and private/protected field names should
359: * be returned
360: * @see #getTypeMember
361: */
362: protected void populateTypeMemberSet(Set s, boolean debugger) {
363: init();
364:
365: for (Iterator itr = memberSet.symbols(); itr.hasNext();)
366: s
367: .add(Symbol.getSymbol(((Integer) (itr.next()))
368: .intValue()));
369: }
370:
371: /*=======================================================================*/
372: /**
373: * Derived classes that implement {@link #getMember} should also
374: * implement this.
375: *
376: * @param s the set to populate
377: * @param debugger <code>true</code> if being used by debugger, in
378: * which case both public and private/protected field names should
379: * be returned
380: * @see #getMember
381: */
382: protected void populateMemberSet(Set s, boolean debugger) {
383: }
384:
385: }
386:
387: /*
388: * Local Variables:
389: * tab-width: 2
390: * indent-tabs-mode: nil
391: * mode: java
392: * c-indentation-style: java
393: * c-basic-offset: 2
394: * eval: (c-set-offset 'substatement-open '0)
395: * eval: (c-set-offset 'case-label '+)
396: * eval: (c-set-offset 'inclass '+)
397: * eval: (c-set-offset 'inline-open '0)
398: * End:
399: */
|