KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > shell > JsValueGlue


1 /*
2  * Copyright 2006 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.shell;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.dev.util.TypeInfo;
20
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.lang.reflect.Field JavaDoc;
23 import java.lang.reflect.InvocationTargetException JavaDoc;
24
25 /**
26  * Glue layer that performs GWT-specific operations on JsValues. Used to isolate
27  * HostedModeExceptions/etc from JsValue code
28  */

29 public class JsValueGlue {
30   public static final String JavaDoc JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject";
31
32   /**
33    * Create a JavaScriptObject instance referring to this JavaScript object.
34    *
35    * The caller is responsible for ensuring that the requested type is a
36    * subclass of JavaScriptObject.
37    *
38    * @param type The subclass of JavaScriptObject to create
39    * @return the constructed JavaScriptObject
40    */

41   public static Object JavaDoc createJavaScriptObject(JsValue value, Class JavaDoc type) {
42     try {
43       // checkThread();
44
if (!value.isJavaScriptObject()) {
45         throw new RuntimeException JavaDoc(
46             "Only Object type JavaScript objects can be made into JavaScriptObject");
47       }
48
49       /* find the JavaScriptObject type, while verifying this is a subclass */
50       Class JavaDoc jsoType = getJavaScriptObjectSuperclass(type);
51       if (jsoType == null) {
52         throw new RuntimeException JavaDoc("Requested type " + type.getName()
53             + " not a subclass of JavaScriptObject");
54       }
55
56       /* create the object using the default constructor */
57       Constructor JavaDoc ctor = type.getDeclaredConstructor(new Class JavaDoc[] {});
58       ctor.setAccessible(true);
59       Object JavaDoc jso = ctor.newInstance(new Object JavaDoc[] {});
60
61       /* set the hostedModeReference field to this JsValue using reflection */
62       Field JavaDoc referenceField = jsoType.getDeclaredField("hostedModeReference");
63       referenceField.setAccessible(true);
64       referenceField.set(jso, value);
65       return jso;
66     } catch (InstantiationException JavaDoc e) {
67       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
68     } catch (IllegalAccessException JavaDoc e) {
69       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
70     } catch (SecurityException JavaDoc e) {
71       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
72     } catch (NoSuchFieldException JavaDoc e) {
73       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
74     } catch (NoSuchMethodException JavaDoc e) {
75       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
76     } catch (IllegalArgumentException JavaDoc e) {
77       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
78     } catch (InvocationTargetException JavaDoc e) {
79       throw new RuntimeException JavaDoc("Error creating JavaScript object", e);
80     }
81   }
82
83   /**
84    * Return an object containing the value JavaScript object as a specified
85    * type.
86    *
87    * @param value the JavaScript value
88    * @param type expected type of the returned object
89    * @param msgPrefix a prefix for error/warning messages
90    * @return the object reference
91    * @throws HostedModeException if the JavaScript object is not assignable to
92    * the supplied type.
93    */

94   public static Object JavaDoc get(JsValue value, Class JavaDoc type, String JavaDoc msgPrefix) {
95     double doubleVal;
96     if (value.isNull()) {
97       return null;
98     }
99     if (value.isUndefined()) {
100       // undefined is never legal to return from JavaScript into Java
101
throw new HostedModeException(msgPrefix
102           + ": JavaScript undefined, expected " + type.getName());
103    }
104     if (value.isWrappedJavaObject()) {
105       Object JavaDoc origObject = value.getWrappedJavaObject();
106       if (!type.isAssignableFrom(origObject.getClass())) {
107         throw new HostedModeException(msgPrefix + ": Java object of type "
108             + origObject.getClass().getName() + ", expected " + type.getName());
109       }
110       return origObject;
111     }
112     if (getJavaScriptObjectSuperclass(type) != null) {
113       if (!value.isJavaScriptObject()) {
114         throw new HostedModeException(msgPrefix + ": JS object of type "
115             + value.getTypeString() + ", expected " + type.getName());
116       }
117       return createJavaScriptObject(value, type);
118     }
119     switch (TypeInfo.classifyType(type)) {
120       case TypeInfo.TYPE_WRAP_BOOLEAN:
121       case TypeInfo.TYPE_PRIM_BOOLEAN:
122         if (!value.isBoolean()) {
123           throw new HostedModeException(msgPrefix + ": JS value of type "
124               + value.getTypeString() + ", expected boolean");
125         }
126         return Boolean.valueOf(value.getBoolean());
127
128       case TypeInfo.TYPE_WRAP_BYTE:
129       case TypeInfo.TYPE_PRIM_BYTE:
130         return new Byte JavaDoc((byte) getIntRange(value, Byte.MIN_VALUE,
131             Byte.MAX_VALUE, "byte", msgPrefix));
132
133       case TypeInfo.TYPE_WRAP_CHAR:
134       case TypeInfo.TYPE_PRIM_CHAR:
135         return new Character JavaDoc((char) getIntRange(value, Character.MIN_VALUE,
136             Character.MAX_VALUE, "char", msgPrefix));
137
138       case TypeInfo.TYPE_WRAP_DOUBLE:
139       case TypeInfo.TYPE_PRIM_DOUBLE:
140         if (!value.isNumber()) {
141           throw new HostedModeException(msgPrefix + ": JS value of type "
142               + value.getTypeString() + ", expected double");
143         }
144         return new Double JavaDoc(value.getNumber());
145
146       case TypeInfo.TYPE_WRAP_FLOAT:
147       case TypeInfo.TYPE_PRIM_FLOAT:
148         if (!value.isNumber()) {
149           throw new HostedModeException(msgPrefix + ": JS value of type "
150               + value.getTypeString() + ", expected float");
151         }
152         doubleVal = value.getNumber();
153         
154         // Check for small changes near MIN_VALUE and replace with the
155
// actual endpoint value, in case it is being used as a sentinel
156
// value. This test works by the subtraction result rounding off to
157
// zero if the delta is not representable in a float.
158
// TODO(jat): add similar test for MAX_VALUE if we have a JS
159
// platform that munges the value while converting to/from strings.
160
if ((float)(doubleVal - Float.MIN_VALUE) == 0.0f) {
161           doubleVal = Float.MIN_VALUE;
162         }
163         
164         float floatVal = (float)doubleVal;
165         if (Float.isInfinite(floatVal) && !Double.isInfinite(doubleVal)) {
166           // in this case we had overflow from the double value which was
167
// outside the range of supported float values, and the cast
168
// converted it to infinity. Since this lost data, we treat this
169
// as an error in hosted mode.
170
throw new HostedModeException(msgPrefix + ": JS value " + doubleVal
171               + " out of range for a float");
172         }
173         return new Float JavaDoc(floatVal);
174
175       case TypeInfo.TYPE_WRAP_INT:
176       case TypeInfo.TYPE_PRIM_INT:
177         return new Integer JavaDoc(getIntRange(value, Integer.MIN_VALUE,
178             Integer.MAX_VALUE, "int", msgPrefix));
179
180       case TypeInfo.TYPE_WRAP_LONG:
181       case TypeInfo.TYPE_PRIM_LONG:
182         if (!value.isNumber()) {
183           throw new HostedModeException(msgPrefix + ": JS value of type "
184               + value.getTypeString() + ", expected long");
185         }
186         doubleVal = value.getNumber();
187         if (doubleVal < Long.MIN_VALUE || doubleVal > Long.MAX_VALUE) {
188           throw new HostedModeException(msgPrefix + ": JS double value " + doubleVal
189               + " out of range for a long");
190         }
191         // TODO(jat): can this actually detect loss of precision?
192
long longVal = (long) doubleVal;
193         if (doubleVal != longVal) {
194           // TODO(jat): should this be an error or exception?
195
ModuleSpace.getLogger().log(TreeLogger.WARN, msgPrefix
196               + ": Loss of precision converting double to long", null);
197         }
198         return new Long JavaDoc(longVal);
199
200       case TypeInfo.TYPE_WRAP_SHORT:
201       case TypeInfo.TYPE_PRIM_SHORT:
202         return new Short JavaDoc((short) getIntRange(value, Short.MIN_VALUE,
203             Short.MAX_VALUE, "short", msgPrefix));
204
205       case TypeInfo.TYPE_WRAP_STRING:
206         if (!value.isString()) {
207           throw new HostedModeException(msgPrefix + ": JS value of type "
208               + value.getTypeString() + ", expected string");
209         }
210         return value.getString();
211
212       case TypeInfo.TYPE_USER:
213         if (value.isString()) {
214           return value.getString();
215         }
216         // if it isn't a String, it's an error, break to error
217
break;
218     }
219
220     // Just don't know what do to with this.
221
throw new IllegalArgumentException JavaDoc(msgPrefix + ": Cannot convert to type "
222         + TypeInfo.getSourceRepresentation(type) + " from "
223         + value.getTypeString());
224   }
225
226   /**
227    * Returns the underlying JsValue from a JavaScriptObject instance.
228    *
229    * The tricky part is that it is in a different classloader so therefore can't
230    * be specified directly. The type is specified as Object, and reflection is
231    * used to retrieve the hostedModeReference field.
232    *
233    * @param jso the instance of JavaScriptObject to retrieve the JsValue from.
234    * @return the JsValue representing the JavaScript object
235    */

236   public static JsValue getUnderlyingObject(Object JavaDoc jso) {
237     try {
238       /*
239        * verify that jso is assignable to
240        * com.google.gwt.core.client.JavaScriptObject
241        */

242       Class JavaDoc type = getJavaScriptObjectSuperclass(jso.getClass());
243
244       if (type == null) {
245         throw new HostedModeException(
246             "Underlying JSO not a subclass of JavaScriptObject");
247       }
248
249       Field JavaDoc referenceField = type.getDeclaredField("hostedModeReference");
250       referenceField.setAccessible(true);
251       return (JsValue) referenceField.get(jso);
252     } catch (IllegalAccessException JavaDoc e) {
253       throw new RuntimeException JavaDoc("Error reading handle", e);
254     } catch (SecurityException JavaDoc e) {
255       throw new RuntimeException JavaDoc("Error reading handle", e);
256     } catch (NoSuchFieldException JavaDoc e) {
257       throw new RuntimeException JavaDoc("Error reading handle", e);
258     }
259   }
260
261   /**
262    * Set the underlying value.
263    *
264    * @param value JsValue to set
265    * @param type static type of the object
266    * @param obj the object to store in the JS value
267    */

268   public static void set(JsValue value, CompilingClassLoader cl, Class JavaDoc type,
269       Object JavaDoc obj) {
270     if (obj == null) {
271       value.setNull();
272     } else if (type.equals(String JavaDoc.class)) {
273       value.setString((String JavaDoc) obj);
274     } else if (type.equals(boolean.class)) {
275       value.setBoolean(((Boolean JavaDoc) obj).booleanValue());
276     } else if (type.equals(short.class)) {
277       value.setInt(((Short JavaDoc) obj).shortValue());
278     } else if (type.equals(int.class)) {
279       value.setInt(((Integer JavaDoc) obj).intValue());
280     } else if (type.equals(byte.class)) {
281       value.setInt(((Byte JavaDoc) obj).byteValue());
282     } else if (type.equals(char.class)) {
283       value.setInt(((Character JavaDoc) obj).charValue());
284     } else if (type.equals(long.class)) {
285       long longVal = ((Long JavaDoc) obj).longValue();
286       double doubleVal = longVal;
287       if ((long) doubleVal != longVal) {
288         // TODO(jat): should this be an error or exception?
289
ModuleSpace.getLogger().log(TreeLogger.WARN,
290             "Loss of precision converting long to double", null);
291       }
292       value.setDouble(doubleVal);
293     } else if (type.equals(float.class)) {
294       value.setDouble(((Float JavaDoc) obj).floatValue());
295     } else if (type.equals(double.class)) {
296       value.setDouble(((Double JavaDoc) obj).doubleValue());
297     } else {
298       // not a boxed primitive
299
try {
300         Class JavaDoc jso = Class.forName(JSO_CLASS, true, cl);
301         if (jso.isAssignableFrom(type) && jso.isAssignableFrom(obj.getClass())) {
302           JsValue jsObject = getUnderlyingObject(obj);
303           value.setValue(jsObject);
304           return;
305         }
306       } catch (ClassNotFoundException JavaDoc e) {
307         // Ignore the exception, if we can't find the class then obviously we
308
// don't have to worry about o being one
309
}
310
311       // Fallthrough case: Object.
312
if (!type.isAssignableFrom(obj.getClass())) {
313           throw new HostedModeException("object is of type "
314               + obj.getClass().getName() + ", expected " + type.getName());
315         }
316       value.setWrappedJavaObject(cl, obj);
317     }
318   }
319
320   private static int getIntRange(JsValue value, int low, int high,
321       String JavaDoc typeName, String JavaDoc msgPrefix) {
322     int intVal;
323     if (value.isInt()) {
324       intVal = value.getInt();
325       if (intVal < low || intVal > high) {
326         throw new HostedModeException(msgPrefix + ": JS int value " + intVal
327             + " out of range for a " + typeName);
328       }
329     } else if (value.isNumber()) {
330       double doubleVal = value.getNumber();
331       if (doubleVal < low || doubleVal > high) {
332         throw new HostedModeException(msgPrefix + ": JS double value "
333             + doubleVal + " out of range for a " + typeName);
334       }
335       intVal = (int) doubleVal;
336       if (intVal != doubleVal) {
337         ModuleSpace.getLogger().log(TreeLogger.WARN, msgPrefix
338             + ": Rounding double to int for " + typeName, null);
339       }
340     } else {
341       throw new HostedModeException(msgPrefix + ": JS value of type "
342           + value.getTypeString() + ", expected " + typeName);
343     }
344     return intVal;
345   }
346
347   /**
348    * Verify that the supplied class is a subclass of
349    * com.google.gwt.core.client.JavaScriptObject, and return the
350    * JavaScriptObject class if it is. This is required since JavaScriptObject
351    * actually lives in a different classloader and can't be referenced directly.
352    *
353    * @param type class to test
354    * @return the JavaScriptObject class object if it is a subclass, or null if
355    * not.
356    */

357   private static Class JavaDoc getJavaScriptObjectSuperclass(Class JavaDoc type) {
358     while (type != null && !type.getName().equals(JSO_CLASS)) {
359       type = type.getSuperclass();
360     }
361     return type;
362   }
363 }
364
Popular Tags