com.google.gwt.dev.shell.designtime.JsValue.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.dev.shell.designtime.JsValue.java

Source

/*******************************************************************************
 * 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.util.Vector;

/**
 * Represents a JavaScript value.
 * 
 * Note that in general the various get*() methods will return
 * platform-independent values only if the corresponding is*() method returns
 * true. In some cases, an IllegalStateException may be thrown if the JavaScript
 * value is not of the appropriate type or bogus values may be returned. Note
 * that getString will try very hard to return a reasonable result for any
 * value, but it is intended only for human consumption and the exact format for
 * anything besides a string value cannot be relied upon.
 */
public abstract class JsValue {
    /**
     * Allows JsValue subclasses to clean themselves up.
     */
    protected interface JsCleanup {
        void doCleanup();
    }

    /**
     * For a thread-safety check to make sure only one thread ever accesses it.
     */
    private static Thread theOnlyThreadAllowed;

    /**
     * A queue of JsCleanup objects ready to be released by the main thread.
     */
    private static Vector<JsCleanup> toBeReleased = new Vector<JsCleanup>();

    private static final Object toBeReleasedLock = new Object();

    /**
     * The main thread should call this from time to time to release hosted-mode
     * objects that Java is no longer referencing.
     */
    public static void mainThreadCleanup() {
        checkThread();
        Vector<JsCleanup> temp;
        synchronized (toBeReleasedLock) {
            temp = toBeReleased;
            toBeReleased = new Vector<JsCleanup>();
        }
        for (JsCleanup cleanup : temp) {
            cleanup.doCleanup();
        }
        temp.clear();
    }

    /**
     * Ensures that the current thread is actually the UI thread.
     */
    private static synchronized void checkThread() {
        if (theOnlyThreadAllowed == null) {
            theOnlyThreadAllowed = Thread.currentThread();
        } else if (theOnlyThreadAllowed != Thread.currentThread()) {
            throw new RuntimeException("This object has permanent thread affinity.");
        }
    }

    /**
     * Moves this JS value to the queue of objects that are ready to be released.
     */
    private static void queueCleanup(JsCleanup cleanup) {
        // Add to the queue to be released by the main thread later.
        //
        synchronized (toBeReleasedLock) {
            toBeReleased.add(cleanup);
        }
    }

    /**
     * Get the value of the object as a boolean. May attempt to convert the value
     * to a boolean if it is not a boolean.
     * 
     * @return the value of the underlying object as a boolean
     */
    public abstract boolean getBoolean();

    /**
     * Get the value of the object as an integer. May attempt to convert the value
     * to an integer if it is not an integer.
     * 
     * @return the value of the underlying object as an int
     */
    public abstract int getInt();

    /**
     * Returns a unique value corresponding to the underlying JavaScript object.
     * In general, two different JsValues will return the same value IFF the
     * underlying JavaScript objects are identical (===).
     * 
     * @return a unique number corresponding to the underlying object, or
     *         <code>0</code> if {@link #isJavaScriptObject()} is
     *         <code>false</code>
     */
    public abstract long getJavaScriptObjectPointer();

    /**
     * Get the value of the object as a double. May attempt to convert the value
     * to a double if it is not a double.
     * 
     * @return the value of the underlying object as a double
     */
    public abstract double getNumber();

    /**
     * Get the value of the object as a string. Will coerce the underlying type to
     * a string, but stable cross-platform behavior is only guaranteed when
     * {@link #isString()} is <code>true</code>.
     * 
     * @return the value of the underlying object as a string
     */
    public abstract String getString();

    /**
     * Returns a human-readable string describing the type of the JS object. This
     * is intended only for human consumption and may vary across platforms.
     */
    public abstract String getTypeString();

    /**
     * Unwrap a wrapped Java object.
     * 
     * @return the original Java object wrapped in this JS object
     */
    public abstract Object getWrappedJavaObject();

    /**
     * Returns true if the JS value is a boolean.
     */
    public abstract boolean isBoolean();

    /**
     * Returns true if getInt() can be used on this value.
     */
    public abstract boolean isInt();

    /**
     * Returns true if the JS value is a native JS object.
     */
    public abstract boolean isJavaScriptObject();

    /**
     * Returns true if the JS value is null.
     */
    public abstract boolean isNull();

    /**
     * Returns true if the JS value is a numeric type.
     */
    public abstract boolean isNumber();

    /**
     * Returns true if the JS value is a string.
     */
    public abstract boolean isString();

    /**
     * Returns true if the JS value is undefined (void).
     */
    public abstract boolean isUndefined();

    /**
     * Returns true if the JS value is a wrapped Java object.
     */
    public abstract boolean isWrappedJavaObject();

    /**
     * Sets the JS object to be a boolean value.
     * 
     * @param val the boolean value to set
     */
    public abstract void setBoolean(boolean val);

    /**
     * Sets the JS object to be a number, passed as an byte.
     * 
     * @param val the value to store
     */
    public abstract void setByte(byte val);

    /**
     * Sets the JS object to be a number, passed as a char.
     * 
     * @param val the value to store
     */
    public abstract void setChar(char val);

    /**
     * Sets the JS object to be a number, passed as a double.
     * 
     * @param val the value to store
     */
    public abstract void setDouble(double val);

    /**
     * Sets the JS object to be a number, passed as an int.
     * 
     * @param val the value to store
     */
    public abstract void setInt(int val);

    /**
     * Set the JS object to be null.
     * 
     * @throws com.google.gwt.dev.shell.HostedModeException
     */
    public abstract void setNull();

    /**
     * Sets the JS object to be a number, passed as a short.
     * 
     * @param val the value to store
     */
    public abstract void setShort(short val);

    /**
     * Set the JS object to the supplied string.
     * 
     * @param val the string to put in the JS object
     * @throws HostedModeException on JS allocation failures
     */
    public abstract void setString(String val);

    /**
     * Set the JS object to be undefined (void).
     * 
     * @throws HostedModeException on JS allocation failures
     */
    public abstract void setUndefined();

    /**
     * Make this JsValue refer to the same underlying object as another JsValue.
     * 
     * @param other JsValue to copy JS object from
     */
    public abstract void setValue(JsValue other);

    /**
     * Set the JS object to the supplied object, which will be wrapped in a
     * platform-dependent JavaScript class.
     * 
     * @param <T> the type of the Java object to wrap
     * @param cl the classloader to create the wrapper object with
     * @param val the Java object to wrap
     * @throws HostedModeException
     */
    public abstract <T> void setWrappedJavaObject(ClassLoader cl, DispatchIdOracle dispIdOracle, T val);

    /**
     * Produce a string representation of the JsValue.
     */
    @Override
    public String toString() {
        if (isUndefined()) {
            return "void";
        } else if (isNull()) {
            return "null";
        } else if (isBoolean()) {
            return "bool: " + (getBoolean() ? "true" : "false");
        } else if (isInt()) {
            return "int: " + Integer.toString(getInt());
        } else if (isNumber()) {
            return "double: " + Double.toString(getNumber());
        } else if (isWrappedJavaObject()) {
            Object wrappedObject = getWrappedJavaObject();
            if (wrappedObject == null) {
                return "Java static dispatch";
            }
            // avoid calling toString on the wrapped object, as this can be expensive
            return "Java object: " + wrappedObject.getClass().getName() + '@'
                    + System.identityHashCode(wrappedObject);
        } else if (isJavaScriptObject()) {
            return getTypeString();
        } else if (isString()) {
            return "string: '" + getString() + "'";
        }
        return getTypeString();
    }

    /**
     * Create an object which frees the underlying JS resource.
     * 
     * @return a JsCleanup object which will free the underlying JS resource
     */
    protected abstract JsCleanup createCleanupObject();

    /**
     * When the Java object is garbage-collected, make sure the associated JS
     * resource is freed. A helper object is used to avoid issues with
     * resurrecting this object.
     */
    @Override
    protected final void finalize() throws Throwable {
        queueCleanup(createCleanupObject());
    }
}