/*
* Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package visage.lang;
import java.util.Vector;
import org.visage.runtime.Entry;
import org.visage.functions.Function0;
import org.visage.runtime.SystemProperties;
import org.visage.runtime.VisageExit;
import org.visage.runtime.VisageObject;
import org.visage.runtime.sequence.Sequence;
/**
* Visage, analogous to java.lang.System, is a place to store static utility methods.
*
* @author Brian Goetz
* @author Saul Wold
*
* @profile common
*/
public class Visage {
/**
* Compare two Visage Objects
*
* @param a the first object to be compared
* @param b the second object to compare
* @return true if they are the same object
*/
public static boolean isSameObject(Object a, Object b) {
return Builtins.isSameObject(a, b);
}
/**
* Print the Object 'val'.
*
* @param val The Object to be printed
*/
public static void print(Object val) {
Builtins.print(val);
}
/**
* Print the Object 'val' and a new-line.
*
* @param val The Object to be printed
*/
public static void println(Object val) {
Builtins.println(val);
}
/**
* Test if an instance variable has been initialized.
*
* @param instance instance to be tested
* @param offset offset of variable to be tested
* @return true if the variable has been initialized
*/
@org.visage.runtime.annotation.VisageSignature("(Ljava/lang/Object;)Z")
public static boolean isInitialized(VisageObject instance, int varOffset) {
return Builtins.isInitialized(instance, varOffset);
}
/**
* Gets the system property indicated by the specified key.
* <p></p>
* System Properties in Visage environment can be classified into 2 types:
* <p>
* 1. Runtime platform associated property.
* Those properties have an equivalent in current java runtime
* environment (SE/ME). The Visage.getProperty() method retrieves
* those properties by mapping specified key with runtime platform key.<br>
* If there is a security manager, property access permission is checked. This may result in a SecurityException.
* </p><p>
* 2. Visage specific property.
* Those properties are specific to Visage environment therefore
* value of the properties is specified in the Visage tree.
* </p>
* <br>
* This set of system properties always includes values for the following keys: <p>
*
* <table summary="Shows property keys and associated values">
* <tr><th>Key</th>
* <th>Description of Associated Value</th>
* <tr><td><code>visage.version</code></td>
* <td><code>Visage release version - visage specific property</code></td></tr>
* <tr><td><code>visage.application.codebase</code></td>
* <td><code>Application codebase - visage specific property</code></td></tr>
* <tr><td><code>visage.java.version</code></td>
* <td><code>Java Runtime Environment version</code></td></tr>
* <tr><td><code>visage.java.vendor</code></td>
* <td><code>Java Runtime Environment vendor</code></td></tr
* <tr><td><code>visage.java.vendor.url</code></td>
* <td><code>Java vendor URL</code></td></tr>
* <tr><td><code>visage.java.io.tmpdir</code></td>
* <td><code>Default temp file path</code></td></tr>
* <tr><td><code>visage.java.ext.dirs</code></td>
* <td><code>Path of extension directory or directories</code></td></tr>
* <tr><td><code>visage.os.name</code></td>
* <td><code>Operating system name</code></td></tr>
* <tr><td><code>visage.os.arch</code></td>
* <td><code>Operating system architecture</code></td></tr>
* <tr><td><code>visage.os.version</code></td>
* <td><code>Operating system version</code></td></tr>
* <tr><td><code>visage.file.separator</code></td>
* <td><code>File separator</code></td></tr>
* <tr><td><code>visage.path.separator</code></td>
* <td><code>Path separator</code></td></tr>
* <tr><td><code>visage.line.separator</code></td>
* <td><code>Line separator</code></td></tr>
* <tr><td><code>visage.user.home</code></td>
* <td><code>User's home directory</code></td></tr>
* <tr><td><code>visage.user.dir</code></td>
* <td><code>User's current working directory</code></td></tr>
* <tr><td><code>visage.timezone</code></td>
* <td><code>User's timezone</code></td></tr>
* <tr><td><code>visage.language</code></td>
* <td><code>User's language</code></td></tr>
* <tr><td><code>visage.region</code></td>
* <td><code>User's region</code></td></tr>
* <tr><td><code>visage.variant</code></td>
* <td><code>User's variant</code></td></tr>
* <tr><td><code>visage.encoding</code></td>
* <td><code>User's encoding</code></td></tr>
* </table>
* <p>
*
* @param key Environment Property to be inquired
* @return The string value of the property
* @throws SecurityException if a security manager exists and its checkPropertyAccess method doesn't allow access to the specified system property.
* @throws NullPointerException if key is null.
* @profile common
*/
public static String getProperty (String key) {
return SystemProperties.getProperty(key);
}
/*
* This static will be unique to each applet, when we move
* to the VISAGEME/Embedded this will need to be tied the AMS
* TODO: for Mobile Guys
*/
private static VisageSystemActionData exitData = new VisageSystemActionData();
/**
* Exits the Script and causes any Shutdown Actions to be called
* This may cause the Runtime to exit as {@code System.exit()}
* depending on the underlying implementation.
* </p><p>
* Any Shutdown Actions that were previously added using the
* {@code addShutdownAction()} function will be exectued at this time
* in LIFO ordering.
* </p><p>
* A second call to {@code Visage.exit()} once {@code Visage.exit()} has
* started will result a {@code IllegalStateException} to be thrown,
* this can occur if a {@code Timeline} calls {@code Visage.exit()} while
* Visage.exit is started.
* If a call to {@code Visage.exit()} occurs in a Shutdown Action, that
* action's function will simply exit without completing the rest of
* its operation and the next Shutdown Action, if any, will run.
* </p><p>
* This function will not normally return to the calling Script.
* </p>
*
* @throws IllegalStateException when called during the process of exiting.
*
* @profile common
*/
public static void exit() {
if (exitData.called) {
throw new IllegalStateException("Can not call Visage.exit() twice");
} else {
exitData.called = true;
}
/*
* Run the exit actions
*/
exitData.runActions();
/*
* Call to Entry.java in order to get the RuntimeProvider
* to do additional cleanup as needed for that provider
*/
Entry.exit();
/*
* Mark this as false for handling restarting from the
* same VM or Browser Context
*/
exitData.called = false;
/*
* Use of VisageExit here is needed because the EDT
* will pass it along rather than catch and quit
*/
throw new VisageExit();
}
/**
* Adds an action to the queue to be executed at {@code Visage.exit()} time
* This action will be added to the queue as a push stack, meaning that
* they will be excuted in FILO ordering. Duplicate actions are
* not allowed and will cause the orignal Handle to be returned with
* no reordering.
*
* @param action of type {@code function():Void} that will be executed
* at {@code Visage.exit()} time. Only one copy of an action can be in
* the queue, an attempt to add the same action a second time will
* return the previous Handle without any reodering.
* @return Handle used to remove the action if needed.
*
* @throws NullPointerException if the action if null
* @profile common
*/
public static int addShutdownAction(Function0<Void> action) {
if (action == null) {
throw new NullPointerException("Action function can not be null");
} else {
return exitData.addAction(action);
}
}
/**
* Removes the action from the queue specified by the actionType parameter.
*
* @param action of type {@code function():Void} that will be removed
* from the Shutdown Action Stack
* @return a Boolean value signifing sucess or failure of removing
* the action
*
* @profile common
*/
public static boolean removeShutdownAction(int handle) {
return exitData.removeAction(handle);
}
/**
* A {@code deferAction} represents an action that should be executed at a
* later time of the system's choosing.
* <p />
* In systems based on event dispatch, such as Swing, execution of a
* {@code deferAction} generally means putting it on the event queue
* for later processing.
*
* @param action of type {@code function():Void} that will be executed
* later based on the implementation.
*
* @throws NullPointerException if the action if null
* @profile common
*/
public static void deferAction(Function0<Void> action) {
if (action == null) {
throw new NullPointerException("Action function can not be null");
} else {
Entry.deferAction(action);
}
}
/**
* For Visage applications that are started on the command
* line,running application. This will return Unnamed Arguments
*
* @return Sequence of commandline args as strings, this will return
* null under the following conditions:
* <ul>
* <li>No Incoming arguments on Command line</li>
* <li>Only Name, Value pairs on the Command line</li>
* </ul>
* @profile common
*/
public static Sequence<? extends String> getArguments() {
return Entry.getArguments();
}
/**
* Returns the named incoming argument for the current Visage
* Script program; this is used for certain environments (in
* particular, applets) where incoming arguments are represented
* as name/value pairs. This usually returns a String, but some
* environments may return other kinds of values. Accepts numbers
* in the form of Strings (e.g. {@code getArgument("0")}) to
* provide unification with {@link #getArguments getArguments}.
* Returns null if the given named argument does not exist.
* </p><p>
* getArgument("visage.applet") will return the underlying applet that is
* used to run the Visage application inside the browser. This is
* an experimental facility, that may be changed in future versions.
* </p><p>
* This can be used as follows:
* </p><p>
* var applet = Visage.getArgument("visage.applet") as java.applet.Applet;
* </p><p>
* Once the applet is obtained, there are 4 suggested ways to use it
* <ol><li>
* to invoke AppletContext's showDocument() method(s)
* </li><li>
* to invoke AppletContext's showStatus() method
* </li><li>
* to retrieve the JSObject to interact with JavaScript in the page
* </li><li>
* to retrieve the DOM object using bootstrapping mechanism in the new plugin
* </li></ol>
* </p><p>
* getArgument("visage.applet") will return null if not running as an applet
* </p><p>
* @return a string representing the value for named or numeric argument,
* or null if given name does not exist.
*
* @profile common
*/
public static Object getArgument(String name) {
return Entry.getArgument(name);
}
/*
* This inner help class is used to store the Action Data needed
* for exitActions, in the future there may me addition System
* Actions that will be required such as Low Resources or ...
*/
private static class VisageSystemActionData {
boolean called;
Vector actions, handles;
private static VisageSystemActionData singleton = null;
private VisageSystemActionData() {
called = false;
actions = new Vector();
handles = new Vector();
}
int addAction(Object action) {
int hash = action.hashCode();
// Check for action already in Vector
int index = handles.indexOf(hash);
if (index != -1) {
// Return the hash without reorder
return hash;
}
actions.addElement(action);
handles.addElement(hash);
return hash;
}
boolean removeAction(int handle) {
if (handles.isEmpty() || !handles.contains(handle)) {
return false;
} else {
try {
int index = handles.indexOf(handle);
if (index != -1) {
actions.removeElementAt(index);
handles.removeElementAt(index);
}
} catch (ArrayIndexOutOfBoundsException e) {
return false;
}
return true;
}
}
void runActions() {
if (actions == null) {
return;
}
while (!actions.isEmpty()) {
Function0<Void> action = null;
// Execute LIFO Action
action = (Function0<Void>) actions.lastElement();
actions.remove(actions.lastIndexOf(action));
if (action != null) {
try {
/*
* TODO: add timer to kill long running Action
*/
action.invoke$(null, null, null);
} catch (Throwable ignore) {
// Ignore all Throwables
}
}
}
handles.removeAllElements();
}
}
}