001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.shell;
017:
018: import java.util.Vector;
019:
020: /**
021: * Represents a JavaScript value.
022: *
023: * Note that in general the various get*() methods will return
024: * platform-independent values only if the corresponding is*() method returns
025: * true. In some cases, an IllegalStateException may be thrown if the JavaScript
026: * value is not of the appropriate type or bogus values may be returned. Note
027: * that getString will try very hard to return a reasonable result for any
028: * value, but it is intended only for human consumption and the exact format for
029: * anything besides a string value cannot be relied upon.
030: */
031: public abstract class JsValue {
032:
033: /**
034: * Allows JsValue subclasses to clean themselves up.
035: */
036: protected interface JsCleanup {
037: void doCleanup();
038: }
039:
040: /**
041: * For a thread-safety check to make sure only one thread ever accesses it.
042: */
043: private static Thread theOnlyThreadAllowed;
044:
045: /**
046: * A queue of JsCleanup objects ready to be released by the main thread.
047: */
048: private static Vector<JsCleanup> toBeReleased = new Vector<JsCleanup>();
049:
050: private static final Object toBeReleasedLock = new Object();
051:
052: /**
053: * The main thread should call this from time to time to release hosted-mode
054: * objects that Java is no longer referencing.
055: */
056: public static void mainThreadCleanup() {
057: checkThread();
058: Vector<JsCleanup> temp;
059: synchronized (toBeReleasedLock) {
060: temp = toBeReleased;
061: toBeReleased = new Vector<JsCleanup>();
062: }
063: for (JsCleanup cleanup : temp) {
064: cleanup.doCleanup();
065: }
066: temp.clear();
067: }
068:
069: /**
070: * Ensures that the current thread is actually the UI thread.
071: */
072: private static synchronized void checkThread() {
073: if (theOnlyThreadAllowed == null) {
074: theOnlyThreadAllowed = Thread.currentThread();
075: } else if (theOnlyThreadAllowed != Thread.currentThread()) {
076: throw new RuntimeException(
077: "This object has permanent thread affinity.");
078: }
079: }
080:
081: /**
082: * Moves this JS value to the queue of objects that are ready to be released.
083: */
084: private static void queueCleanup(JsCleanup cleanup) {
085: // Add to the queue to be released by the main thread later.
086: //
087: synchronized (toBeReleasedLock) {
088: toBeReleased.add(cleanup);
089: }
090: }
091:
092: /**
093: * Get the value of the object as a boolean. May attempt to convert the value
094: * to a boolean if it is not a boolean.
095: *
096: * @return the value of the underlying object as a boolean
097: */
098: public abstract boolean getBoolean();
099:
100: /**
101: * Get the value of the object as an integer. May attempt to convert the value
102: * to an integer if it is not an integer.
103: *
104: * @return the value of the underlying object as an int
105: */
106: public abstract int getInt();
107:
108: /**
109: * Get the value of the object as a double. May attempt to convert the value
110: * to a double if it is not a double.
111: *
112: * @return the value of the underlying object as a double
113: */
114: public abstract double getNumber();
115:
116: /**
117: * Get the value of the object as a string. Tries very hard to return a
118: * reasonable value for any underyling type, but this should only be used for
119: * human consumption as the exact format is not reliable across platforms.
120: *
121: * @return the value of the underlying object as a string
122: */
123: public abstract String getString();
124:
125: /**
126: * Returns a human-readable string describing the type of the JS object. This
127: * is intended only for human consumption and may vary across platforms.
128: */
129: public abstract String getTypeString();
130:
131: /**
132: * Unwrap a wrapped Java object.
133: *
134: * @return the original Java object wrapped in this JS object
135: */
136: public abstract Object getWrappedJavaObject();
137:
138: /**
139: * Returns true if the JS value is a boolean.
140: */
141: public abstract boolean isBoolean();
142:
143: /**
144: * Returns true if getInt() can be used on this value.
145: */
146: public abstract boolean isInt();
147:
148: /**
149: * Returns true if the JS value is a native JS object.
150: */
151: public abstract boolean isJavaScriptObject();
152:
153: /**
154: * Returns true if the JS value is null.
155: */
156: public abstract boolean isNull();
157:
158: /**
159: * Returns true if the JS value is a numeric type.
160: */
161: public abstract boolean isNumber();
162:
163: /**
164: * Returns true if the JS value is a string.
165: */
166: public abstract boolean isString();
167:
168: /**
169: * Returns true if the JS value is undefined (void).
170: */
171: public abstract boolean isUndefined();
172:
173: /**
174: * Returns true if the JS value is a wrapped Java object.
175: */
176: public abstract boolean isWrappedJavaObject();
177:
178: /**
179: * Sets the JS object to be a boolean value.
180: *
181: * @param val the boolean value to set
182: */
183: public abstract void setBoolean(boolean val);
184:
185: /**
186: * Sets the JS object to be a number, passed as an byte.
187: *
188: * @param val the value to store
189: */
190: public abstract void setByte(byte val);
191:
192: /**
193: * Sets the JS object to be a number, passed as a char.
194: *
195: * @param val the value to store
196: */
197: public abstract void setChar(char val);
198:
199: /**
200: * Sets the JS object to be a number, passed as a double.
201: *
202: * @param val the value to store
203: */
204: public abstract void setDouble(double val);
205:
206: /**
207: * Sets the JS object to be a number, passed as an int.
208: *
209: * @param val the value to store
210: */
211: public abstract void setInt(int val);
212:
213: /**
214: * Set the JS object to be null.
215: *
216: * @throws HostedModeException
217: */
218: public abstract void setNull();
219:
220: /**
221: * Sets the JS object to be a number, passed as a short.
222: *
223: * @param val the value to store
224: */
225: public abstract void setShort(short val);
226:
227: /**
228: * Set the JS object to the supplied string.
229: *
230: * @param val the string to put in the JS object
231: * @throws HostedModeException on JS allocation failures
232: */
233: public abstract void setString(String val);
234:
235: /**
236: * Set the JS object to be undefined (void).
237: *
238: * @throws HostedModeException on JS allocation failures
239: */
240: public abstract void setUndefined();
241:
242: /**
243: * Make this JsValue refer to the same underlying object as another JsValue.
244: *
245: * @param other JsValue to copy JS object from
246: */
247: public abstract void setValue(JsValue other);
248:
249: /**
250: * Wrap a Java method as a JavaScript function pointer.
251: *
252: * @param string the name of the method
253: * @param dispMethod the DispatchMethod object describing the method tow wrap
254: */
255: // TODO(jat): platform-independent version of this?
256: // The problem is that each platform has different conventions for passing
257: // JavaScript values back into a Java method.
258: // public abstract void setWrappedFunction(String string,
259: // DispatchMethod dispMethod);
260: /**
261: * Set the JS object to the supplied object, which will be wrapped in a
262: * platform-dependent JavaScript class.
263: *
264: * @param <T> the type of the Java object to wrap
265: * @param cl the classloader to create the wrapper object with
266: * @param val the Java object to wrap
267: * @throws HostedModeException
268: */
269: public abstract <T> void setWrappedJavaObject(
270: CompilingClassLoader cl, T val);
271:
272: /**
273: * Produce a string representation of the JsValue.
274: */
275: @Override
276: public String toString() {
277: if (isUndefined()) {
278: return "void";
279: } else if (isNull()) {
280: return "null";
281: } else if (isBoolean()) {
282: return "bool: " + (getBoolean() ? "true" : "false");
283: } else if (isInt()) {
284: return "int: " + Integer.toString(getInt());
285: } else if (isNumber()) {
286: return "double: " + Double.toString(getNumber());
287: } else if (isWrappedJavaObject()) {
288: return "Java object: " + getWrappedJavaObject().toString();
289: } else if (isJavaScriptObject()) {
290: return "JS object [" + getTypeString() + "] : "
291: + getString();
292: } else if (isString()) {
293: return "string: '" + getString() + "'";
294: } else {
295: return "*unknown type: " + getTypeString() + "*";
296: }
297: }
298:
299: /**
300: * Create an object which frees the underlying JS resource.
301: *
302: * @return a JsCleanup object which will free the underlying JS resource
303: */
304: protected abstract JsCleanup createCleanupObject();
305:
306: /**
307: * When the Java object is garbage-collected, make sure the associated JS
308: * resource is freed. A helper object is used to avoid issues with
309: * resurrecting this object.
310: */
311: @Override
312: protected final void finalize() throws Throwable {
313: queueCleanup(createCleanupObject());
314: }
315: }
|