Tutorial of J2S in Eclipse (7): Advanced Programming on J2S -- @j2s* and others

Zhou Renjian
May 17, 2006

This article will describe more details on using Java2Script Pacemaker, including writing and debuging native JavaScript. Developer will learn how to debug and optimize their web applications beside the level of Java but also in level of JavaScript. This article aims those advanced Java2Script users.

Prerequisite

1. Eclipse 3.1.1 SDK (Maybe Eclipse 3.1 SDK also be OK)
2. Java2Script 1.0.0 M2+ (M2 is not released at the time of writing this article, just try to setup Java2Script environment from Subversion repository.

@j2s* in JavaDoc block

Java2Script extends the JavaDoc, and you can use the extended JavaDoc tags to do some jobs in JavaScript way. With the help of extended JavaDoc tag, we can add extra JavaScript codes or replacing the Java codes without modifying the Java codes.

Now, the extended JavaDoc tags includes @j2sNative, @j2sOverride, @j2sDebug, @j2sIgnore, @j2sIgnoreSuperConstructor, @j2sKeep. More tags may be developed.

@j2sNative

By using @j2sNative, you can bind some AJAX calling or integrating with other libraries, and any things that JavaScript does.

OK, just read those JavaDoc comments inside the source and think what can we do:

package net.sf.j2s.tutorial.debug;

public class HelloJ2SNative {
	/**
	 * For java native methods, if Java2Script native codes are given, the
	 * native method will be generated when Java2Script compiler is enabled
	 *
	 * @j2sNative
	 * var styleCSS = "position:absolute;left:150px;top:5px;width:100px;height:40px;" 
	 *		+ "text-align:center;font-weight:bold;color:yellow;background-color:blue;" 
	 *		+ "border:1px solid red;";
	 * var hiEl = document.createElement("<div style=\"" +  styleCSS + "\"></div>");
	 * document.body.appendChild(hiEl);
	 * hiEl.appendChild (document.createTextNode ("Hi!"));
	 */
	native static void sayHi();
	
	/**
	 * For java native methods, if you did not define Java2Script native codes,
	 * the method will be simply ignored when generating the JavaScript codes.
	 */
	native static void sayWell();
	
	/**
	 * When j2sNative is defined fro a method, the method body will be overrided
	 * with the given native JavaScript codes.
	 *
	 * @j2sNative alert ("Hello, JavaScript");
	 */
	static void sayHello() {
		System.out.println("Hello, Java");
	}
	
	static void sayHei(String name) {
		if(name != null) 
		/**
		 * For a normal block, inert j2sNative Javadoc before the brace will
		 * replace the inner block body.
		 *
		 * @j2sNative alert ("Hei, " + name); 
		 */
		{
			System.out.println("Hei, " + name + ", how are you?");
		} else
		/**
		 * @j2sNative alert ("Hei.");
		 */
		{
			System.out.println("Hei, how are you?");
		}
	}
	
	static void saySomething() {
		/**
		 * Insert JavaScript only codes with an empty brace block.
		 *
		 * @j2sNative alert ("En, things will go fine.");
		 */ 
		{}
	}
	
	public static void main(String[] args) {
		/*
		 * When running from Java, the following line must be commented, as
		 * there are not implemented native codes for the native method #sayHi
		 */
		sayHi();
		sayHello();
		sayHei("Josson");
		saySomething();
	}
}

The generated JavaScript by Java2Script compiler:

Clazz.declarePackage ("net.sf.j2s.tutorial.debug");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "HelloJ2SNative");
cla$$.sayHi = Clazz.defineMethod (cla$$, "sayHi", 
function () {
var styleCSS = "position:absolute;left:150px;top:5px;width:100px;height:40px;"
+ "text-align:center;font-weight:bold;color:yellow;background-color:blue;"
+ "border:1px solid red;";
var hiEl = document.createElement("<div style=\"" +  styleCSS + "\"></div>");
document.body.appendChild(hiEl);
hiEl.appendChild (document.createTextNode ("Hi!"));
});
cla$$.sayHello = Clazz.defineMethod (cla$$, "sayHello", 
function () {
alert ("Hello, JavaScript");
});
cla$$.sayHei = Clazz.defineMethod (cla$$, "sayHei", 
function (name) {
if (name != null) {
alert ("Hei, " + name);
} else {
alert ("Hei.");
}}, "String");
cla$$.saySomething = Clazz.defineMethod (cla$$, "saySomething", 
function () {
{
alert ("En, things will go fine.");
}});
cla$$.main = Clazz.defineMethod (cla$$, "main", 
function (args) {
net.sf.j2s.tutorial.debug.HelloJ2SNative.sayHi ();
net.sf.j2s.tutorial.debug.HelloJ2SNative.sayHello ();
net.sf.j2s.tutorial.debug.HelloJ2SNative.sayHei ("Josson");
net.sf.j2s.tutorial.debug.HelloJ2SNative.saySomething ();
}, "Array");

@j2sOverride

The @j2sOverride is for JavaScript inheritance performance optimization only. Reader may skip this paragraph.

package net.sf.j2s.tutorial.debug;

class A {
	public void sayHi() {
		System.out.println("Hi from class A");
	}
}

class B extends A {
	/**
	 * The B#sayHi can not ignore A#sayHi, as it calls
	 * super#sayHi, which is A#sayHi
	 */
	public void sayHi() {
		System.out.println("Hi from class B");
		super.sayHi();
	}
}

class C extends A {
	/**
	 * The C#sayHi can ignore A#sayHi.
	 * The generated JavaScript for this method will be
	 * Clazz.overrideMethod(...)
	 */
	public void sayHi() {
		System.out.println("Hi from class C");
	}
}

class D extends A {
	public void sayHello() {
		super.sayHi();
	}
	
	/**
	 * The B#sayHi can not ignore A#sayHi, as there are another method 
	 * #sayHello calls super#sayHi, which is A#sayHi
	 */
	public void sayHi() {
		System.out.println("Hi from class D");
	}
}

class E {
	public void sayHi() {
		saySomething();
	}
	
	private void saySomething() {
		System.out.println("Say something privately from class E");
	}
}

class F extends E {
	/**
	 * As there are a private method #saySomething in the super class,
	 * F#saySomething need to keep the super class' private information
	 * to make inheritance work correctly. So Java2Script compiler will
	 * always generate Clazz.defineMethod(...) instead of
	 * Clazz.overrideMethod(...) 
	 * 
	 * To keep private method is being correctly executed in JavaScript, we
	 * have to pay extra time in checking. For example, when an instance of
	 * F is calling #sayHi, which results in calling E#sayHi, and in the
	 * method body of E#sayHi, it calls private method #saySomething, but 
	 * now the #saySomething has already been updated by F#saySomething.
	 * In normal inheritance, calling #saySomething on instance of class F
	 * should result in calling F#saySomething. But E#sayHi is calling private
	 * method. So F#saySomething should not be called! In such situation, 
	 * extra checking for private method is performed in F#saySomething. In
	 * the generated JavaScript of F#saySomething, extra script:
	 * var $private = Clazz.checkPrivateMethod (arguments);
	 * if ($private != null) {
	 * 	return $private.apply (this, arguments);
	 * }
	 * is inserted to the beginning of the method body to make sure private
	 * methods are being executed correctly.
	 * 
	 * For more information about how to keep class' private method working
	 * in Java2Script, please read more codes on Clazz#checkPrivateMethod in
	 * j2s-core-*.z.js.
	 */
	public void saySomething() {
		System.out.println("Say something publicly from class F");
	}
}

class G extends F {
	/**
	 * So Java2Script compiler will always generate 
	 * Clazz.defineMethod(...) instead of
	 * Clazz.overrideMethod(...) 
	 */
	public void saySomething() {
		System.out.println("Say something publicly from class G");
	}
}

class H {
	public void sayHi() {
		System.out.println("Say Hi from class H");
	}
	
	public void sayHi(String name) {
		System.out.println("Say Hi with name from class H");
	}
}

class I extends H {
	/**
	 * Though I#sayHi and other parts of H do not call H#sayHi,
	 * there are some where else (Maybe subclass of I) may call 
	 * H#say(String). So I#sayHi can not ignore the H#syaHi, and
	 * the generated JavaScript will be
	 * Clazz.defineMethod(...)
	 */
	public void sayHi() {
		System.out.println("Say Hi from class I");
	}
}


class J extends H {
	/**
	 * Subclasses won't have their chances to call H#sayHi or
	 * H#sayHi(String), so J#sayHi can ignore H#sayHi and H#sayHi(String).
	 * But the Java2Script compiler won't be smart to this level
	 * to detect such method declaration, it is up to you to make
	 * such compiling optimization.
	 * 
	 * @j2sOverride
	 */
	public void sayHi() {
		System.out.println("Say Hi from class J");
	}
	
	/*
	 * j2sOverride should only be declared once when it meets the first
	 * method with the same name in the same class.
	 * j2sOverride must not be declared here, or class J will lose information
	 * about J#sayHi.
	 */
	public void sayHi(String name) {
		System.out.println("Say Hi with name from class H");
	}
}

public class HelloJ2SOverride {
	public static void main(String[] args) {
		// No test
	}
}

And the generated JavaScript codes:

Clazz.declarePackage ("net.sf.j2s.tutorial.debug");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "A");
Clazz.defineMethod (cla$$, "sayHi", 
function () {
System.out.println ("Hi from class A");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "B", net.sf.j2s.tutorial.debug.A);
Clazz.defineMethod (cla$$, "sayHi", 
function () {
System.out.println ("Hi from class B");
Clazz.superCall (this, net.sf.j2s.tutorial.debug.B, "sayHi", []);
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "C", net.sf.j2s.tutorial.debug.A);
Clazz.overrideMethod (cla$$, "sayHi", 
function () {
System.out.println ("Hi from class C");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "D", net.sf.j2s.tutorial.debug.A);
Clazz.defineMethod (cla$$, "sayHello", 
function () {
Clazz.superCall (this, net.sf.j2s.tutorial.debug.D, "sayHi", []);
});
Clazz.defineMethod (cla$$, "sayHi", 
function () {
System.out.println ("Hi from class D");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "E");
Clazz.defineMethod (cla$$, "sayHi", 
function () {
this.saySomething ();
});
Clazz.defineMethod (cla$$, "saySomething", 
($fz = function () {
System.out.println ("Say something privately from class E");
}, $fz.isPrivate = true, $fz));
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "F", net.sf.j2s.tutorial.debug.E);
Clazz.defineMethod (cla$$, "saySomething", 
function () {
var $private = Clazz.checkPrivateMethod (arguments);
if ($private != null) {
return $private.apply (this, arguments);
}
System.out.println ("Say something publicly from class F");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "G", net.sf.j2s.tutorial.debug.F);
Clazz.defineMethod (cla$$, "saySomething", 
function () {
var $private = Clazz.checkPrivateMethod (arguments);
if ($private != null) {
return $private.apply (this, arguments);
}
System.out.println ("Say something publicly from class G");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "H");
Clazz.defineMethod (cla$$, "sayHi", 
function () {
System.out.println ("Say Hi from class H");
});
Clazz.defineMethod (cla$$, "sayHi", 
function (name) {
System.out.println ("Say Hi with name from class H");
}, "String");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "I", net.sf.j2s.tutorial.debug.H);
Clazz.defineMethod (cla$$, "sayHi", 
function () {
System.out.println ("Say Hi from class I");
});
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "J", net.sf.j2s.tutorial.debug.H);
Clazz.overrideMethod (cla$$, "sayHi", 
function () {
System.out.println ("Say Hi from class J");
});
Clazz.defineMethod (cla$$, "sayHi", 
function (name) {
System.out.println ("Say Hi with name from class H");
}, "String");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "HelloJ2SOverride");
cla$$.main = Clazz.defineMethod (cla$$, "main", 
function (args) {
}, "Array");

@j2sDebug

The @j2sDebug is much similar with @j2sNative, but @j2sDebug only be enabled when the compiler mode is set to "debug". To enable "debug" mode for Java2Scrit compiler, you have to open the file .j2s, switch to "Source" tab and insert a new line:

j2s.compiler.mode=debug
And then save and rebuild the full project or modify specific Java files to get @j2sDebug enabled.

When both @j2sDebug and @j2sNative are enable for a method or block, then @j2sDebug will have a high priority be generated, and codes declared by @j2sNative will be ignored.

@j2sIgnore and @j2sIgnoreSuperConstructor

The @j2sIgnore can be used similar to @j2sNative, but @j2sIgnore just ignore the whole method declaration (include constructors) or the whole block.

The @j2sIgnoreSuperConstructor can only be used for constructor method. And its function is to ignore those implicit or explicit "super(...)" expression.

package net.sf.j2s.tutorial.debug;

class Z {
	/**
	 * The whole method of #justDeclare will not declared for
	 * class Z in JavaScript.
	 * 
	 * @j2sIgnore
	 */
	void justDeclare() {
		System.out.println("Will be ignored by JavaScript");
	}
}

class Y {
	int y;
	
	/**
	 * Constructor does not do anything, should be ignored.
	 * 
	 * @j2sIgnore
	 */
	public Y() {
	}
	
	public Y(int x, int y) {
		/*
		 * The following super() is to be ignored, as there are no explicit
		 * super() constructor declaration. Object's default constructor will
		 * always be ignored.
		 */
		super();  
		this.y = x + y;
	}
}

class X extends Y {

	public X() {
		// Here there will be an implicit super constructor call.
	}
	
	/**
	 * @j2sIgnoreSuperConstructor
	 * ignore the implicit super constructor 
	 */
	public X(Object o) {
		// Here there will be an implicit super constructor call.
	}
	
	/**
	 * @param y
	 * @j2sIgnoreSuperConstructor 
	 * Super constructor does nothing, so can be ignored.
	 */
	public X(int y) {
		super(); // to be ignored in JavaScript by @j2sIgnoreSuperConstructor
		this.y = y;
	}
	
	public X(int x, int y) {
		super(); // to be kept in JavaScript
		this.y = x - y;
	}
	
}
public class HelloJ2SIgnore {
	public static void main(String[] args) {
		/**
		 * @j2sIgnore
		 */
		{
			System.out.println("Hi");
		}
	}
}

The generated JavaScript:

Clazz.declarePackage ("net.sf.j2s.tutorial.debug");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "Z");
cla$$ = Clazz.decorateAsClass (function () {
this.y = 0;
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "Y");
Clazz.makeConstructor (cla$$, 
function (x, y) {
this.y = x + y;
}, "Number,Number");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "X", net.sf.j2s.tutorial.debug.Y);
Clazz.makeConstructor (cla$$, 
function () {
Clazz.superConstructor (this, net.sf.j2s.tutorial.debug.X, []);
});
Clazz.makeConstructor (cla$$, 
function (o) {
}, "Object");
Clazz.makeConstructor (cla$$, 
function (y) {
this.y = y;
}, "Number");
Clazz.makeConstructor (cla$$, 
function (x, y) {
Clazz.superConstructor (this, net.sf.j2s.tutorial.debug.X);
this.y = x - y;
}, "Number,Number");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "HelloJ2SIgnore");
cla$$.main = Clazz.defineMethod (cla$$, "main", 
function (args) {
{
}}, "Array");

These two options are also for JavaScript performance optimization. Because calling constructors (those default constructors that do nothing) cost some CPU times. Reducing the unnecessary constructor calling will help a lot.

@j2sKeep

Private methods that which never be used locally will not be generated into JavaScript. But @j2sKeep will try to tell the compiler to keep the method for some certain reasons, for example, reflection calls or private methods are just for JavaScript only.

package net.sf.j2s.tutorial.debug;

public class HelloJ2SKeep {
	/**
	 * Private methods which never be used locally will not be generated into
	 * JavaScript codes in order to reduce file size and improve performance.
	 */
	private static void noTouch() {
		System.out.println("Won't be called");
	}
	
	private static void touch() {
		System.out.println("Called");
	}
	
	/**
	 * Maybe method will be called by reflection.
	 * 
	 * @j2sKeep
	 */
	private static void keepMeImportant() {
		System.out.println("Just keep me!");
	}

	public static void main(String[] args) {
		touch();
	}
}

The generated JavaScript:

Clazz.declarePackage ("net.sf.j2s.tutorial.debug");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "HelloJ2SKeep");
cla$$.touch = Clazz.defineMethod (cla$$, "touch", 
($fz = function () {
System.out.println ("Called");
}, $fz.isPrivate = true, $fz));
cla$$.keepMeImportant = Clazz.defineMethod (cla$$, "keepMeImportant", 
($fz = function () {
System.out.println ("Just keep me!");
}, $fz.isPrivate = true, $fz));
cla$$.main = Clazz.defineMethod (cla$$, "main", 
function (args) {
net.sf.j2s.tutorial.debug.HelloJ2SKeep.touch ();
}, "Array");

alert(), log(), error()

The three method alert(), log(), error() are provided so that user can insert debuging codes into those *.js more quickly. And the original window.alert() is kept as popupAlert(), and the new alert() is to write message into console without UI blocking.

alert() is same as System.out.println(), and error() is same as System.err.println(). But alert() and error() are much shorter.

public class HelloJ2SAlertLogError {
	
	public static void main(String[] args) {
		System.out.println("Hello System.out.println");
		System.err.println("Hello System.err.println");
		/**
		 * @j2sNative
		 * alert ("alert");
		 * log ("log");
		 * error ("error");
		 * popupAlert ("popupAlert");
		 */ {}
	}
}

The generated JavaScript:

Clazz.declarePackage ("net.sf.j2s.tutorial.debug");
cla$$ = Clazz.decorateAsClass (function () {
Clazz.instantialize (this, arguments);
}, net.sf.j2s.tutorial.debug, "HelloJ2SAlertLogError");
cla$$.main = Clazz.defineMethod (cla$$, "main", 
function (args) {
System.out.println ("Hello System.out.println");
System.err.println ("Hello System.err.println");
{
alert ("alert");
log ("log");
error ("error");
popupAlert ("popupAlert");
}}, "Array");

More tips on Java2Script

We need more conventions

OK. More and more conventions will be developed to give developers an efficient Java/JavaScript programming.

Blog discussion

Have problems while reading through these articles? Or find some errors between the words? Or have some ideas on the Java2Script technology? Please make comments or discussions here in the blog.

About the author

Zhou Renjian: Initial contributor of Java2Script.