Section 23.3. Scripting with Java


23.3. Scripting with Java

Having explored how to control Java from JavaScript code, let's now turn to the opposite problem: how to control JavaScript from a Java applet. All Java interactions with JavaScript are handled through an instance of the netscape.javascript.JSObject class. (Complete documentation for JSObject is in Part IV.) An instance of this class is a wrapper around a single JavaScript object. The class defines methods that allow you to read and write property values and array elements of the JavaScript object and to invoke methods of the object. Here is a synopsis of this class:

 public final class JSObject extends Object {     // This static method returns an initial JSObject for the browser window     public static JSObject getWindow(java.applet.Applet applet);     // These instance methods manipulate the object     public Object getMember(String name);              // Read object property     public Object getSlot(int index);                  // Read array element     public void setMember(String name, Object value);  // Set object property     public void setSlot(int index, Object value);      // Set array element     public void removeMember(String name);             // Delete property     public Object call(String name, Object args[]);    // Invoke method     public Object eval(String s);                      // Evaluate string     public String toString( );                            // Convert to string     protected void finalize( ); } 

The JSObject class does not have a constructor. A Java applet obtains its first JSObject with the static getWindow() method. When passed a reference to an applet, this method returns a JSObject that represents the browser window in which the applet appears. Thus, every applet that interacts with JavaScript includes a line that looks something like this:

 JSObject win = JSObject.getWindow(this);  // "this" is the applet itself 

Having obtained a JSObject that refers to the Window object, you can use instance methods of this initial JSObject to obtain other JSObjects representing other JavaScript objects:

 import netscape.javascript.JSObject;  // This must be at the top of the file ... // Get the initial JSObject representing the Window JSObject win = JSObject.getWindow(this);             // window // Use getMember( ) to get a JSObject representing the Document object JSObject doc = (JSObject)win.getMember("document");  // .document // Use call( ) to get a JSObject representing an element of the document JSObject div = (JSObject)doc.call("getElementById",  // .getElementById('test')                                   new Object[] { "test" }); 

Note that getMember() and call() both return a value of type Object, which generally must be cast to some more specific type, such as JSObject. Also note that when you invoke a JavaScript method with call(), you pass arguments as an array of Java Object values. This array is required even if the method you are invoking expects one argument or none at all.

The JSObject class has one more important method: eval(). This Java method works like the JavaScript function of the same name: it executes a string that contains JavaScript code. You'll find that using eval( ) is often much easier than using the other methods of the JSObject class. For example, consider the following use of eval( ) to set a CSS style on a document element:

 JSObject win = JSObject.getWindow(this); win.eval("document.getElementById('test').style.backgroundColor = 'gray';"); 

Doing the same thing without the eval() method takes more code:

 JSObject win = JSObject.getWindow(this);             // window JSObject doc = (JSObject)win.getMember("document");  // .document JSObject div = (JSObject)doc.call("getElementById",  // .getElementById('test')                                   new Object[] { "test" }); JSObject style = (JSObject)div.getMember("style");   // .style style.setMember("backgroundColor", "gray");          // .backgroundColor="gray" 

23.3.1. Compiling and Deploying Applets That Use JSObject

In order to deploy any applet, you must compile it and then embed it in an HTML file. When an applet uses the JSObject class, special care is required for both of these steps.

To compile an applet that interacts with JavaScript, you must tell the compiler where to find the netscape.javascript.JSObject class. When browsers shipped with their own integrated Java interpreter, this was a complicated matter. However, now that all browsers use Sun's Java plug-in, it is much simpler. The JSObject class is in the jre/lib/plugin.jar file of the Java distribution. So, to compile an applet that uses JSObject, use a command like this, substituting the Java installation directory used on your system:

 %javac -cp /usr/local/java/jre/lib/plugin.jar ScriptedApplet.java 

There is an additional requirement for running an applet that interacts with JavaScript. As a security precaution, an applet is not allowed to use JavaScript unless the web page author (who may not be the applet author) explicitly gives the applet permission to do so. To give this permission, you include the mayscript attribute in the applet's <applet> (or <object> or <embed>) tag. For example:

 <applet code="ScriptingApplet.class" mayscript width="300" height="300"> </applet> 

If you forget the mayscript attribute, the applet is not allowed to use the JSObject class.

23.3.2. Java-to-JavaScript Data Conversion

The JSObject class must convert Java values to JavaScript values when it invokes a method or sets a field, and convert JavaScript values back to Java values when it returns from a method or reads a field. The conversions performed by JSObject are different from the LiveConnect conversions documented in Chapter 12. Unfortunately, the conversions performed by JSObject are also more platform-dependent than the conversions performed when JavaScript scripts Java.

When Java reads a JavaScript value, the conversions are straightforward:

  • JavaScript numbers convert to java.lang.Double.

  • JavaScript strings convert to java.lang.String.

  • JavaScript boolean values convert to java.lang.Boolean.

  • The JavaScript null value converts to the Java null value.

  • The JavaScript undefined value converts in a platform-dependent way: with the Java 5 plug-in, undefined converts to null in Internet Explorer but to the string "undefined" with Firefox.

When Java sets a JavaScript property or passes an argument to a JavaScript method, the conversions ought to be similarly straightforward. Unfortunately, they are platform-dependent. Using Firefox 1.0 and the Java 5 plug-in, Java values are not converted, and JavaScript sees them as JavaObject objects (which it can interact with using LiveConnect):

Using IE 6 and the Java 5 plug-in, the conversion is more sensible:

  • Java numbers and characters convert to JavaScript numbers.

  • Java String objects convert to JavaScript strings.

  • Java boolean values convert to JavaScript boolean values.

  • The Java null value converts to the JavaScript null value.

  • Any other Java values convert to JavaObjects.

Because of this discrepancy between Firefox and IE, you should should take care to convert values as necessary in your JavaScript code. When writing a JavaScript function that will be invoked by an applet, for example, you should explicitly convert the arguments, using the Number(), String(), and Boolean() conversion functions before using them.

One way to avoid the whole issue of data conversion is to use the JSObject.eval() method whenever your Java code wants to communicate with JavaScript.

23.3.3. The Common DOM API

In Java version 1.4 and later, the Java plug-in includes the Common DOM API, which is a Java implementation of DOM Level 2 that is layered on top of the netscape.javascript.JSObject class. It allows Java applets to interact with the document in which they are embedded using the Java binding of the DOM.

This is an exciting idea, but, unfortunately, its implementation leaves much to be desired. One serious problem is that the implementation (in both IE and Firefox) seems to be incapable of creating a new Text node or obtaining an existing Text node, making the Common DOM API useless for querying or modifying the content of a document. Some DOM manipulations are supported, however, and Example 23-3 demonstrates how an applet can use the Common DOM API to set a CSS style on an HTML element.

There are a few points to note about this example. First, the API for manipulating the DOM is somewhat unusual. You place your DOM code in the run() method of a DOMAction object. You then pass this DOMAction object to a method of a DOMService object. When your run() method is invoked, it is passed a DOMAccessor object you can use to obtain the Document object, which is the root of the DOM object hierarchy.

The second thing you'll notice about Example 23-3 is that the Java binding of the DOM API is significantly more verbose and awkward than the JavaScript binding of the same API. Finally, note that the task accomplished by this example would be more easily accomplished by passing a line of JavaScript code to the JSObject.eval() method!

The code in Example 23-3 does not make explicit use of the JSObject class, and it compiles without any additions to the classpath.

Example 23-3. An applet using the Common DOM API

 import java.applet.Applet;          // The Applet class itself import com.sun.java.browser.dom.*;  // The Common DOM API import org.w3c.dom.*;               // The W3C core DOM API import org.w3c.dom.css.*;           // The W3C CSS DOM API // This applet does nothing on its own. It simply defines a method for // JavaScript code to call. That method then uses the Common DOM API to // manipulate the document in which the applet is embedded. public class DOMApplet extends Applet {     // Set the background of the element with the specified id to the     // specified color.     public void setBackgroundColor(final String id, final String color)         throws DOMUnsupportedException, DOMAccessException     {         // We start with a DOMService object, which we obtain like this         DOMService service = DOMService.getService(this);         // Then we call invokeAndWait( ) on the DOMService, passing a DOMAction         service.invokeAndWait(new DOMAction( ) {                 // The DOM code we want to execute is in the run( ) method                 public Object run(DOMAccessor accessor) {                     // We use the DOMAccessor to get the Document object                     // Note that we pass the applet object as an argument                     Document d = accessor.getDocument(DOMApplet.this);                     // Get the element we want to manipulate                     Element e = d.getElementById(id);                     // Cast the element to an ElementCSSInlineStyle so we can                     // call its getStyle( ) method. Then cast the return value                     // of that method to a CSS2Properties object.                     CSS2Properties style =                         (CSS2Properties) ((ElementCSSInlineStyle)e).getStyle( );                     // Finally, we can set the backgroundColor property                     style.setBackgroundColor(color);                     // A DOMAction can return a value, but this one doesn't                     return null;                 }             });     } } 




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net