Java tutorial
/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.google.gwt.dev.shell.designtime; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /** * Glue layer that performs GWT-specific operations on JsValues. Used to isolate * HostedModeExceptions/etc from JsValue code */ public final class JsValueGlue { public static final String HOSTED_MODE_REFERENCE = "hostedModeReference"; public static final String JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject"; public static final String JSO_IMPL_CLASS = "com.google.gwt.core.client.JavaScriptObject$"; /** * Create a JavaScriptObject instance referring to this JavaScript object. * * @param classLoader * the classLoader to create from * @return the constructed JavaScriptObject */ public static Object createJavaScriptObject(JsValue value, ClassLoader classLoader) { Throwable caught; try { // See if there's already a wrapper object (assures identity comparison). Object jso = WrappersCache.getCachedJso(classLoader, value.getJavaScriptObjectPointer()); if (jso != null) { return jso; } // Instantiate the JSO class. Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, true, classLoader); Constructor<?> ctor = jsoType.getDeclaredConstructor(); ctor.setAccessible(true); jso = ctor.newInstance(); // Set the reference field to this JsValue using reflection. Field referenceField = jsoType.getField(HOSTED_MODE_REFERENCE); referenceField.set(jso, value); WrappersCache.putCachedJso(classLoader, value.getJavaScriptObjectPointer(), jso); return jso; } catch (InstantiationException e) { caught = e; } catch (IllegalAccessException e) { caught = e; } catch (SecurityException e) { caught = e; } catch (NoSuchMethodException e) { caught = e; } catch (IllegalArgumentException e) { caught = e; } catch (InvocationTargetException e) { caught = e; } catch (ClassNotFoundException e) { caught = e; } catch (NoSuchFieldException e) { caught = e; } throw new RuntimeException("Error creating JavaScript object", caught); } /** * Return an object containing the value JavaScript object as a specified type. * * @param value * the JavaScript value * @param type * expected type of the returned object * @param msgPrefix * a prefix for error/warning messages * @return the object reference * @throws com.google.gwt.dev.shell.HostedModeException * if the JavaScript object is not assignable to the supplied type. */ @SuppressWarnings("unchecked") public static <T> T get(JsValue value, ClassLoader cl, Class<T> type, String msgPrefix) { if (type.isPrimitive()) { if (type == Boolean.TYPE) { if (!value.isBoolean()) { throw new RuntimeException( msgPrefix + ": JS value of type " + value.getTypeString() + ", expected boolean"); } return (T) Boolean.valueOf(value.getBoolean()); } else if (type == Byte.TYPE) { return (T) Byte .valueOf((byte) getIntRange(value, Byte.MIN_VALUE, Byte.MAX_VALUE, "byte", msgPrefix)); } else if (type == Character.TYPE) { return (T) Character.valueOf( (char) getIntRange(value, Character.MIN_VALUE, Character.MAX_VALUE, "char", msgPrefix)); } else if (type == Double.TYPE) { if (!value.isNumber()) { throw new RuntimeException( msgPrefix + ": JS value of type " + value.getTypeString() + ", expected double"); } return (T) Double.valueOf(value.getNumber()); } else if (type == Float.TYPE) { if (!value.isNumber()) { throw new RuntimeException( msgPrefix + ": JS value of type " + value.getTypeString() + ", expected float"); } double doubleVal = value.getNumber(); // Check for small changes near MIN_VALUE and replace with the // actual end point value, in case it is being used as a sentinel // value. This test works by the subtraction result rounding off to // zero if the delta is not representable in a float. // TODO(jat): add similar test for MAX_VALUE if we have a JS // platform that alters the value while converting to/from strings. if ((float) (doubleVal - Float.MIN_VALUE) == 0.0f) { doubleVal = Float.MIN_VALUE; } float floatVal = (float) doubleVal; if (Float.isInfinite(floatVal) && !Double.isInfinite(doubleVal)) { // in this case we had overflow from the double value which was // outside the range of supported float values, and the cast // converted it to infinity. Since this lost data, we treat this // as an error in hosted mode. throw new RuntimeException(msgPrefix + ": JS value " + doubleVal + " out of range for a float"); } return (T) Float.valueOf(floatVal); } else if (type == Integer.TYPE) { return (T) Integer .valueOf(getIntRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE, "int", msgPrefix)); } else if (type == Long.TYPE) { if (!value.isWrappedJavaObject()) { throw new RuntimeException( msgPrefix + ": JS value of type " + value.getTypeString() + ", expected Java long"); } JavaLong javaLong = (JavaLong) value.getWrappedJavaObject(); return (T) Long.valueOf(javaLong.longValue()); } else if (type == Short.TYPE) { return (T) Short .valueOf((short) getIntRange(value, Short.MIN_VALUE, Short.MAX_VALUE, "short", msgPrefix)); } } if (value.isNull() || value.isUndefined()) { return null; } if (value.isWrappedJavaObject()) { return type.cast(value.getWrappedJavaObject()); } if (value.isString()) { return type.cast(value.getString()); } if (value.isJavaScriptObject()) { return type.cast(createJavaScriptObject(value, cl)); } // Just don't know what do to with this. /* * TODO (amitmanjhi): does throwing a HostedModeException here and catching * a RuntimeException in user test * com.google.gwt.dev.jjs.test.HostedTest::testObjectReturns() make sense */ throw new IllegalArgumentException(msgPrefix + ": JS value of type " + value.getTypeString() + ", expected " + getSourceRepresentation(type)); } /** * Set the underlying value. * * @param value * JsValue to set * @param type * static type of the object * @param obj * the object to store in the JS value */ public static void set(JsValue value, ClassLoader cl, DispatchIdOracle dispIdOracle, Class<?> type, Object obj) { if (type.isPrimitive()) { if (type == Boolean.TYPE) { value.setBoolean(((Boolean) obj).booleanValue()); } else if (type == Byte.TYPE) { value.setInt(((Byte) obj).byteValue()); } else if (type == Character.TYPE) { value.setInt(((Character) obj).charValue()); } else if (type == Double.TYPE) { value.setDouble(((Double) obj).doubleValue()); } else if (type == Float.TYPE) { value.setDouble(((Float) obj).floatValue()); } else if (type == Integer.TYPE) { value.setInt(((Integer) obj).intValue()); } else if (type == Long.TYPE) { long longVal = ((Long) obj).longValue(); value.setWrappedJavaObject(cl, dispIdOracle, new JavaLong(longVal)); } else if (type == Short.TYPE) { value.setInt(((Short) obj).shortValue()); } else if (type == Void.TYPE) { value.setUndefined(); } else { throw new RuntimeException("Cannot marshal primitive type " + type); } } else if (obj == null) { value.setNull(); } else { // not a boxed primitive try { Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, false, cl); if (jsoType == obj.getClass()) { JsValue jsObject = getUnderlyingObject(obj); value.setValue(jsObject); return; } } catch (ClassNotFoundException e) { // Ignore the exception, if we can't find the class then obviously we // don't have to worry about o being one } // Fall through case: Object. if (!type.isInstance(obj)) { throw new RuntimeException( "object is of type " + obj.getClass().getName() + ", expected " + type.getName()); } if (obj instanceof String) { value.setString((String) obj); } else { value.setWrappedJavaObject(cl, dispIdOracle, obj); } } } private static int getIntRange(JsValue value, int low, int high, String typeName, String msgPrefix) { int intVal; if (value.isInt()) { intVal = value.getInt(); if (intVal < low || intVal > high) { throw new RuntimeException( msgPrefix + ": JS int value " + intVal + " out of range for a " + typeName); } } else if (value.isNumber()) { double doubleVal = value.getNumber(); if (doubleVal < low || doubleVal > high) { throw new RuntimeException( msgPrefix + ": JS double value " + doubleVal + " out of range for a " + typeName); } intVal = (int) doubleVal; if (intVal != doubleVal) { /* FIXME: ModuleSpace.getLogger().log( TreeLogger.WARN, msgPrefix + ": Rounding double (" + doubleVal + ") to int for " + typeName, null);*/ } } else { throw new RuntimeException( msgPrefix + ": JS value of type " + value.getTypeString() + ", expected " + typeName); } return intVal; } /** * Returns the underlying JsValue from a JavaScriptObject instance. * * The tricky part is that it is in a different ClassLoader so therefore can't be specified * directly. The type is specified as Object, and reflection is used to retrieve the reference * field. * * @param jso * the instance of JavaScriptObject to retrieve the JsValue from. * @return the JsValue representing the JavaScript object */ private static JsValue getUnderlyingObject(Object jso) { Throwable caught; try { Field referenceField = jso.getClass().getField(HOSTED_MODE_REFERENCE); referenceField.setAccessible(true); return (JsValue) referenceField.get(jso); } catch (IllegalAccessException e) { caught = e; } catch (SecurityException e) { caught = e; } catch (NoSuchFieldException e) { caught = e; } throw new RuntimeException("Error reading " + HOSTED_MODE_REFERENCE, caught); } private JsValueGlue() { } private static String getSourceRepresentation(Class<?> type) { // Primitives // if (type.equals(Integer.TYPE)) { return "int"; } else if (type.equals(Long.TYPE)) { return "long"; } else if (type.equals(Short.TYPE)) { return "short"; } else if (type.equals(Byte.TYPE)) { return "byte"; } else if (type.equals(Character.TYPE)) { return "char"; } else if (type.equals(Boolean.TYPE)) { return "boolean"; } else if (type.equals(Float.TYPE)) { return "float"; } else if (type.equals(Double.TYPE)) { return "double"; } // Arrays // if (type.isArray()) { Class<?> componentType = type.getComponentType(); return getSourceRepresentation(componentType) + "[]"; } // Everything else // return type.getName().replace('$', '.'); } }