Now that you know how to install and run Java2Script from the sources, and know how to export your own version of Java2Script, we will describe a mechanism supported by Java2Script eclipse plugin that will let us extend the compiler easily, without modifying the compiler sources and without having to learn all compiler related classes internals for doing it.
Note: Original Java2Script articles about this topic are are the following:
http://j2s.sourceforge.net/articles/tutorial-extended-compiler.html
http://blog.java2script.org/2006/10/31/extending-java2script-compiler/
In this section you will learn how to customize how to customize the output of j2s compiler. You will be able to make an eclipse plugin that contribute to Java2Script Compiler with custom code for performing the translation of desired java language elements, like methods, classes, fields, expressions, statements, comments, etc to javascript. The main benefit of using an independent eclipse plugin is that you can isolate your customization code from java2script compiler nd so the same java2script distribution can be used for different java to javascript compiling scenarios.
Basically, this eclipse plugin must indicate what it want to customize, and provide the actual code translation for that parts. For example,
analize each method invocation and add some javascript code before or after the invocation.
analize each javadoc comment and search for a certain string or javadoc annotation inside it and add some javascript code before or after the java element documented with that javadoc
add javascript code before or after a class declaration for classes that apply some conditions
customize how java classes must be loaded in certain circunstances
For developing the eclipse plugin we will use the eclipse plugin environment (PDE) that comes with common eclipse distributions. It is recommended but not mandatory to be familiarized with PDE, so you may want to read Plug-in Development Environment Guide .
We will also will need to model java elements like classes, methods, attributes, expressions, statements. For this, we will use eclipse Java Develpment Tools (JDT) and so, for taking full advantadge of this, it is recommended to know the basics about JDT. So, you may want to read JDT programmers guide
In this section we will create an eclipse plugin that contributes to Java2Script compiler with a very simple behavior: it will add a javascript comment before each method invocation, and then the normal method invocation javascript code.
so select File->New->Project...->Plugin Project
Press "next" button.
Press "next" button and in the following step make sure to choose "No" to "Would you like to create a rich client application?":
Press finnish for creating your new J2S compiler extension plugin. Now we want to add "org.eclipse.jdt.core" and "net.sf.j2s.core" dependencies to out new plugin. For this, double click on file META-INF/MANIFEST.MF will open the eclipse plugin editor. Goto the "Dependencies" tab and add "org.eclipse.jdt.core" and "net.sf.j2s.core" as the dependencies:
Note: At this point you have created an J2S extension plugin general project. All previous steps must be performed identically for all your J2s extension plugins.
Now we want to add an extension point from where our plugin will be plugged to the java2script compiler. Let's detail a little how Java2Script works and how our extenison is plugged into the java to javascript compiler.
First of all, the java2script compiler uses eclipse JDT java compiler for parsing all java code into a tree of java elements , called an Abstract syntax tree (AST). For example, a java compilation unit can contain a class declaration, with methods and fields declarations. A method contains a body, and the body some statements. Statements are composed by expresions, etc. In the following figure, the eclipse Outline view shows some of this java elements graphically:
Then the compiler uses "visitors" for converting java source codes to javascript. A visitor is an object which class contains overloaded "visit" methods for visiting each of java element types. Defining new visitors, you can overwrite the "visit" methods corresponding to the desired java element type that you want to customize its translation to javascript. Java2Script uses TWO kind of visitors for doing the total translation from java to javascript:
ASTScriptVisitor. In one side we have an ASTScriptVisitor that is responsible for converting basic java elements to the corresponding javascript code. J2S provides with the default implementation net.sf.j2s.core.astvisitors.SWTScriptVisitor that can be extended for building customized AST script visitors.
DependencyASTVisitor. The other type if visitor is DependencyASTVisitor that is responsible for creating a class dependency trees, in other words an import list for each java file. J2S provides with a default implemenation net.sf.j2s.core.astvisitors.SWTDependencyVisitor. It is recommended that the user extends this class for defining its own AST dependency visitors.
go to the "extensions" tab and add the extension point net.sf.j2s.core.extendedASTScriptVisitor:
This extension point will allow us to provide with a class that will return the kind of visitor your extension want to provide. So choose the right id and class name. The id is very important because you must refence it in each Java2Script project that must be builded using this compiler extension, so remember it. In our name is "j2s.extension1.MethodInvocationComment1". Now click on "class" link for creating the new class:
Pressing finnish button for creating the class. This class msut implement interface net.sf.j2s.core.compiler.IExtendedVisitor. and should return the two visitors, ASTScriptVisitor and DependencyASTVisitor, to use by the Java2Script compiler for converting java to javascript.
In our example, we want only to provide a ScriptVisitor for changing how the java element "method invocation" is translated to javascript. We will use the default DependencyVisitor since we don't want customize anything of the java dependency tree:
Now, the only thing left is to create our custom Script Visitor that we named MethodInvocationComment1ScriptVisitor, so create the class:
And that's it, your compiler extension is ready for use. You can export and install it as any other eclipse plugin. As usual, for exporting the extension, right click our project j2s.extension1 -> export... -> Plug-in development -> Deployable plug-ins and fragments. This will generate a plugins folder in the choosen destination directory with the extension plugin inside. You can redistribute it so other Java2Script users can install it in their systems like any other eclipse plugin.
Important: For using this extension in a Jav2Script project you must indicate the id of your J2S extension in your project's .j2s file. In our case you should add the following line to the .j2s file of the projects you want to compile with the extension:
j2s.compiler.visitor=j2s.extension1.MethodInvocationComment1
Now let's see how our J2S extension translate the following java class:
package j2s.extension1; import org.eclipse.jdt.core.dom.MethodInvocation; import net.sf.j2s.core.astvisitors.ASTScriptVisitor; public class MethodInvocationComment1ScriptVisitor extends ASTScriptVisitor { @Override public boolean visit(MethodInvocation node) { buffer.append("/* A COMMENT ADDED WITH our J2S compiler extension plugin */\n"); return super.visit(node); } } |
and the resulting javascript is
Clazz.declarePackage ("org.foo"); c$ = Clazz.declareType (org.foo, "Test1"); c$.main = Clazz.defineMethod (c$, "main", function (args) { /* A COMMENT ADDED WITH our J2S compiler extension plugin */ java.lang.System.out.println ("hee"); }, "~A"); |
As you can see, out javascript custom comment was appended just before the method declaration at java.lang.System.out.println ("hee");
In this second example we show how to load string from javadoc comments into java fields. In web applications, we usually have to reference html, json, xml or other web format strings in our code and sometimes it is nasty to do it in common java or javascript strings literals.
The following is the java class code that implements ASTScriptVisitor that will detect and load a string in javadoc after the tag @j2sLoadString .
package j2s.extension1; import java.util.Iterator; import java.util.List; import net.sf.j2s.core.astvisitors.SWTScriptVisitor; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; public class MethodInvocationComment1ScriptVisitor extends SWTScriptVisitor { @Override public boolean visit(FieldDeclaration node) { boolean ret = super.visit(node); /* first visit the default visitor implementation */ Javadoc javadoc = node.getJavadoc(); List tags = javadoc.tags(); if(tags!=null&&tags.size()>0) { for (Iterator it = tags.iterator(); it.hasNext();) { Object o = (Object) it.next(); if(o instanceof TagElement) { TagElement tag = (TagElement) o; String tagName = tag.getTagName(); if(tagName!=null&&tagName.equals("@j2sLoadString")) { /* annotation found. all the following javadoc comment until other tag is found will be loaded */ StringBuffer sb = new StringBuffer(); List frags = tag.fragments(); for (Iterator it2 = frags.iterator(); it2.hasNext();) { sb.append(it2.next()+"\\n"); } List fieldDeclFrags = node.fragments(); /* now that we have the @j2sLoadString string, we add the javascript statement this.attributeName="str.." for loading the string field.*/ if(fieldDeclFrags!=null&&fieldDeclFrags.size()>0) { VariableDeclarationFragment f1 = (VariableDeclarationFragment) fieldDeclFrags.get(0); String str = sb.toString(); buffer.append("this."+f1.getName()+"=\'"); buffer.append(str); buffer.append("\';\n"); } } } } } return ret; } } |
Using this compiler extension, you will be able to code complex strings like json, html, xml, etc inside javadoc. The following java test program define two fields, htmlCode1 and jsonCode1 which content is loaded from javadoc and not from string literals. As you can see there is much easier to put other language code inside javadoc instead using java string, in which you have to quote and concatenate:
package org.foo; public class Test1 { public static void main(String[] args) { Test1 test1 = new Test1(); System.out.println("loaded html code: "+test1.getHtmlCode1()); System.out.println("loaded json code: "+test1.getJsonCode1()); System.out.println("lot easier isn't it?"); } /** this field will be loaded with the fololowing string: * @j2sLoadString * <h1>this is an <b>html</b> string loaded from javadoc</h1> */ String htmlCode1; /** * this other too * @j2s LoadString * {p1: [1, 2, 3], p2: {p21: "thi sis a json loaded from javadoc"}} */ String jsonCode1; public String getHtmlCode1() { return htmlCode1; } public String getJsonCode1() { return jsonCode1; } } |
Compiling this using our new compiler extension, the output will be the following when executing this as a Java2Script application:
loaded html code: <h1>this is an <b>html</b> string loaded from javadoc</h1> loaded json code: {p1: [1, 2, 3], p2: {p21: "thi sis a json loaded from javadoc"}} lot easier isn't it? |
I hope the reader can realize how helpfull this particular example can be, how easy it was to develope an independent Java2Script compiler extension that, in this particular case introduced a new @j2s directive for defining string class fields inside javadocs.