In order to understand how LiveConnect does its amazing job of connecting JavaScript to Java, you've got to understand the five JavaScript data types that LiveConnect uses. (There is also a Java data type that LiveConnect uses to connect Java back to JavaScript; we'll learn about that Java class later in this chapter.) The following subsections explain these JavaScript data types. Once we've explored these LiveConnect fundamentals, the following sections will show how we can actually use LiveConnect to connect JavaScript to Java.
The JavaScript JavaPackage object represents a Java package, which is a collection of related Java classes. The properties of a JavaPackage are the classes that the package contains (classes are represented by the JavaClass object, which we'll see later), as well as any other packages that the package contains. A restriction on the JavaPackage object is that you cannot use a JavaScript for/in loop to obtain a complete list of all packages and classes that a JavaPackage contains. The reason for this restriction will become clear in a moment.
All JavaPackage objects are contained within a parent JavaPackage, and the Window property named Packages is a top-level JavaPackage that serves as the root of this package hierarchy. It has java, sun, and netscape properties, which are JavaPackage objects that represent the various hierarchies of Java classes that are included with Navigator. For example, the JavaPackage Packages contains the JavaPackage Packages.java, which contains the JavaPackage Packages.java.awt. For convenience, every Window object has java, sun, and netscape properties which are shortcuts to Packages.java, Packages.sun, and Packages.netscape. Thus, instead of typing Packages.java.awt, you can simply use java.awt.
To continue with the example, java.awt is a JavaPackage object that contains JavaClass objects like java.awt.Button, which represents the java.awt.Button class. But it also contains yet another JavaPackage object, java.awt.image which represents the java.awt.image package in Java.
As you can see, the property naming scheme for the JavaPackage hierarchy mirrors the naming scheme for Java packages. Note that there is one big difference between the JavaPackage object and actual Java packages. Packages in Java are collections of classes, not collections of other packages. That is, java.lang is the name of a Java package, but java is not. So the JavaPackage object named java does not actually represent a package in Java, but is simply a convenient placeholder in the package hierarchy for other JavaPackage objects that do represent real Java packages.
On many systems, Java classes are installed in files in a directory hierarchy that corresponds to the package name. For example, the java.lang.String class is stored in the file java/lang/String.class in my Java implementation from Sun. In other implementations, notably that from Netscape, the class files are actually stored in a large uncompressed zip file. The directory hierarchy is still there, encoded in the file; it is just not visible on the surface. Therefore, instead of thinking of the JavaPackage object as representing a Java package, you may find it clearer to consider it as representing a directory in the Java class hierarchy.
As we've said above, a JavaPackage object contains properties for each of the packages and classes it contains. If you think of a JavaPackage as representing a directory in the Java class directory hierarchy, then the properties of the JavaPackage are the contents of the directory. Each subdirectory of the directory becomes a JavaPackage property, with the package name matching the subdirectory name. Each file in the directory becomes a JavaClass property, with the property name matching the file name, after the .class extension is stripped off. When viewed in this way, it is easy to understand why the JavaPackage object does not allow the for/in loop to list all of its properties--those properties actually correspond to directory contents, and they are not actually looked up and created until they are first used. Thus, a for/in loop will only find those properties of a JavaPackage object that have already been used at least once by the program.
The JavaClass object is a JavaScript representation of a Java class. A JavaClass object does not have any properties of its own--all of its properties represent (and have the same name as) the public static fields and methods of the represented Java class. These public static fields and methods are sometimes called class fields and class methods to indicate that they are associated with an object class rather than an object instance. Unlike the JavaPackage object, the JavaClass object does allow the use of the for/in loop to enumerate its properties. Note that the JavaClass object does not have properties representing the instance fields and methods of a Java class--individual instances of a Java class are represented by the JavaObject object, which will be documented below.
As we saw above, JavaClass objects are contained in JavaPackage objects. For example, java.lang is a JavaPackage that contains a System property. Thus java.lang.System is a JavaClass object, representing the Java class java.lang.System. This JavaClass object, in turn, has properties such as out and in that represent static fields of the java.lang.System class. You can use JavaScript to refer to any of the standard Java system classes in this same way. The java.lang.Double class is named java.lang.Double (or Packages. java.lang.Double) in JavaScript, for example, and the java.awt.Button class is java.awt.Button.
Another way to obtain a JavaClass object in JavaScript is to use the getClass() function. Given any JavaObject, you can obtain a JavaClass that represents the class of that Java object by passing the JavaObject to getClass().
Once you have a JavaClass object, there are several things you can do with it. The JavaClass object implements the LiveConnect functionality that allows JavaScript programs to read and write the public static fields of Java classes, and to invoke the public static methods of Java classes. For example, java.lang.System is a JavaClass. We can read the value of a static field of this class like this:
var java_console = java.lang.System.out;
var java_version = java.lang.System.getProperty("java.version");
There is one more important feature of the JavaClass object. You can use it with the JavaScript new operator to create new instances of Java classes--i.e., to create JavaObject objects. The syntax for doing so is just as it is in JavaScript (and just as it is in Java):
var d = new java.lang.Double(1.23);
Finally, having created a JavaObject in this way, we can return to the getClass() function and show an example of its use:
var d = new java.lang.Double(1.23); // Create a JavaObject. var d_class = getClass(d); // Obtain the JavaClass of the JavaObject. if (d_class == java.lang.Double) ...; // This comparison will be true.
The JavaObject object is a JavaScript object that represents a Java object (that is, it represents an instance of a Java class). The JavaObject object is, in many ways, analogous to the JavaClass object. Like JavaClass, a JavaObject object has no properties of its own--all of its properties represent (and have the same names as) the public instance fields and public instance methods of the Java object it represents. Like JavaClass, you can use a JavaScript for/in loop to enumerate all properties of a JavaObject object. The JavaObject object implements the LiveConnect functionality that allows us to read and write the public instance fields and invoke the public methods of a Java object.
For example, if d is a JavaObject that, as above, represents an instance of the java.lang.Double class, then we can invoke a method of that Java object with JavaScript code like this:
n = d.doubleValue();
java.lang.System.out
java.lang.System.out.println("Hello world!");
[1] The output of this line of code doesn't appear in the web browser itself, but in the "Java Console." Select Show Java Console in the Options menu to make the console visible.
The JavaObject object also allows us to read and write public instance fields of the Java object it represents. Neither the java.lang.Double class or the java.io.PrintStream class used in the examples above has any public instance fields, however. But suppose we use JavaScript to create an instance of the java.awt.Rectangle class:
r = new java.awt.Rectangle();
r.x = r.y = 0; r.width = 4; r.height = 5; var perimeter = 2*r.width + 2*r.height;
The JavaMethod object represents a Java method. In the sections above, we've said that the JavaClass and JavaObject objects provide the LiveConnect functionality that allows JavaScript programs to invoke public class methods and public instance methods. In fact, that claim was an over-simplification. The JavaClass and JavaObject objects contain properties that have the same names as the class and instance fields and the class and instance methods of a Java class or object. The properties that represent fields allow us to read and write class and instance fields. The properties that represent methods, on the other hand, simply contain JavaMethod objects, and it is these JavaMethod objects that actually implement the LiveConnect functionality that lets us invoke Java class and instance methods.
So, when we write lines of JavaScript code like this one:
java.lang.System.out.println("Hello world!");
var println_method = java.lang.System.out.println; println_method("Hello world!");
The LiveConnect functionality provided by the JavaMethod object is substantial. Consider the following JavaScript code:
var r = java.awt.Rectangle(0, 0, 10, 10); // a 10x10 square at (0,0) var i = r.inside(5,5); // is the point (5,5) inside?
JavaMethod objects behave much like regular JavaScript functions, with a few important differences. Java methods, unlike JavaScript functions, expect a fixed number of arguments of a fixed type. If you pass the wrong number or wrong type of arguments, you will cause a JavaScript error. There is a more subtle difference between Java methods and JavaScript functions as well. When a JavaScript function is assigned to an object property, it becomes a method, and is passed a reference to that object as the value of the this keyword. Thus, a JavaScript function may behave differently depending upon which object it is assigned as a property of. This is not true of JavaMethod object--they are invoked in the context of a Java object, and they carry that context with them. A JavaMethod will behave the same regardless of what JavaScript object it is a property of.
The final LiveConnect datatype for JavaScript is the JavaArray object. As you might expect by now, this object represents a Java array, and provides the LiveConnect functionality that allows JavaScript to read the elements of a Java array. Like JavaScript arrays (and like Java arrays), a JavaArray object has a length property that specifies the number of elements it contains. The elements of a JavaArray object are read with the standard JavaScript [] array index operator. They can also be enumerated with the for/in loop. You can also use JavaArray objects to access multidimensional arrays (actually arrays of arrays) just as you would in JavaScript or in Java.
For example, suppose we create an instance of the java.awt.Polygon class:
p = new java.awt.Polygon();
for(int i = 0; i < p.xpoints.length; i++) p.xpoints[i] = Math.round(Math.random()*100); for(int i = 0; i < p.ypoints.length; i++) p.ypoints[i] = Math.round(Math.random()*100);