How to Extend the Java2Script compiler

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,

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

Creating a Java2Script compiler extension eclipse plugin

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

Figure 11-12. Creating a new plugin eclipse project

Press "next" button.

Figure 11-13. Giving a name to the plugin

Press "next" button and in the following step make sure to choose "No" to "Would you like to create a rich client application?":

Figure 11-14. new eclipse plugin project last step

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:

Figure 11-15. eclipse plugin dependencies tab.

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:

Figure 11-16. eclipse outline view showing some java elements of a compilation unit

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:

Figure 11-17. eclipse plugin - adding extension point

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:

Figure 11-18. eclipse plugin - setting extension point name

Figure 11-19. eclipse plugin - creating extension point 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:


package j2s.extension1;

import net.sf.j2s.core.astvisitors.ASTScriptVisitor;
import net.sf.j2s.core.astvisitors.DependencyASTVisitor;
import net.sf.j2s.core.astvisitors.SWTDependencyASTVisitor;
import net.sf.j2s.core.compiler.IExtendedVisitor;

public class MethodInvocationComment1 implements IExtendedVisitor {

  public MethodInvocationComment1() {
  }

  public ASTScriptVisitor getScriptVisitor() {            (1)
    return new MethodInvocationComment1ScriptVisitor();
  }

  public DependencyASTVisitor getDependencyVisitor() {    (2)
    return new DependencyASTVisitor();
  }
}
(1)
we will use our own Script visitor, MethodInvocationComment1ScriptVisitor that we must create extending the default Script visitor net.sf.j2s.core.astvisitors.ASTScriptVisitor
(2)
use the default J2S dependency visitor, net.sf.j2s.core.astvisitors.DependencyASTVisitor

Now, the only thing left is to create our custom Script Visitor that we named MethodInvocationComment1ScriptVisitor, so create the class:


package j2s.extension1;

import org.eclipse.jdt.core.dom.MethodInvocation;

import net.sf.j2s.core.astvisitors.SWTScriptVisitor;

public class MethodInvocationComment1ScriptVisitor extends SWTScriptVisitor {
  @Override
  public boolean visit(MethodInvocation node) {           (1)
    buffer.append("/* A COMMENT ADDED WITH our J2S compiler extension plugin */\n");(2)
    return super.visit(node);                             (3)
  }
}

(1)
Overriding method visit(MethodInvocation node) we can customize how java method invocations are translated to javascript.
(2)
the StringBuilder buffer field contain the actual javascript code. So we write our custom javascript comment before the method translation.
(3)
invoking super.visit(node) will invoke the default implementation for java method invocation translation. We do it after writing our custom javascript code.

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");

Other example: html attributes with javadoc annotations

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.