package elide;

Tutorial: Getting Started With ELIDE

import download.elide;

class Documentation
{
       /** ELIDE Javadoc */	


   String introduction;
   String tutorial;
}
Introduction
This tutorial introduces the ELIDE Java preprocessor. ELIDE is a tool for incrementally extending Java with new design vocabulary: in particular, it allows the introduction of new modifier keywords which can trigger complex transformations on Java code.In this tutorial, we will show how to use ELIDE to introduce a simple "property" keyword that adds JavaBean style accessors to any instance variable.
class Tutorial
  extends Documentation
{
     void runningElide();
}
For example, the following class definition:

public class ElideTest {
	property<> public int x;
	property<> public String[] y;
}

will generate the following code:

public class ElideTest
	implements Serializable
{
	private int x;
	private String[] y;
	
	public int getX()
	{
		return x; 
	}
	
	public void setX(int x)
	{
		this.x = x;
	}
	
	public String[] getY()
	{
		return y; 	
	}

	public void setY(String[] y)
	{
		this.y = y;
	}
}

This example will take you from basic ELIDE concepts and API up some of the more advanced features of the tool. First, before we examine the framework underlying this language extension, let's make sure ELIDE is installed correctly on your system and that everything is working properly. You might also want to look over the Introduction to ELIDE Concepts.

Running ELIDE

ELIDE is packaged as a single jar file, elide.jar. To run ELIDE, make sure that this jar file is in the current directory, and run "java -jar elide.jar".

ELIDE takes a list of Java files to be processed on the command line. As a test, create a file named ElideTest.java with the following code:


public class ElideTest
{
	public int x;
	public String[] y;
}

Now run ELIDE on the file:

> java -jar elide.jar ElideTest.java

ELIDE will create a directory called "output", within which you will find a new ElideTest.java file. It should be identical to the file you created, with minor changes in indentation and line breaks. This is as it should be: ELIDE does no processing of normal Java code.

Adding a new keyword to Java

To get ELIDE to do something to your code, you must introduce a new extension to Java. This is done by subclassing ELIDE's Transform class. Since we want to introduce the "property" keyword, we need to create a file called Property.java. The skeleton should look like this:

package tutorial;
import ca.ubc.cs.elide.*;
import ca.ubc.cs.elide.nodes.*;

public class Property extends Transform
{
}

This code isn't going to do much, of course. We'll extend it later. For now, modify your ElideTest file so that it looks like the version at the very beginning of the tutorial - that is, add "property<>" in front of each instance variable declaration:

public class ElideTest {
	property<> public int x;
	property<> public String[] y;
}

Note that all of the keywords introduced by ELIDE are treated in exactly the same way as modifiers like "public" and "private", so that you could in fact write "public property<> int x" just as well as "property<> public int x", and in general can string ELIDE keywords together anywhere you would put a modifier; the only difference is that ELIDE keywords always have "<>" at the end, which may contain an argument list for more complex transformations.

Adding a transformation method to the Transform

Each subclass of Transform can have any number of transformation methods; these are the methods that specify the effects of a new keyword. Transformation methods must have two properties:
  • The first argument must be a ParseNode, or a subclass of ParseNode. Usually, it will be one of ClassNode, MethodNode, or FieldNode, since classes, methods and fields are the most common items to attach modifiers to.
  • It must be registered with ELIDE's dependency mechanism. For now, that simply means attaching the special keyword "satisfies".
Here is an example transformation method; add it to your Property class:

satisfies<fieldAccess>
public void makeFieldPrivate(FieldNode target)
{
	target.makePrivate();
}

The word "fieldAccess" inside satisfies<> is a description of the type of transformation this method is performing. It is used to determine the order in which to perform some transformations; for more information, see the
Introduction to ELIDE Concepts.

The method itself is extremely simple: it takes a FieldNode as a parameter, which represents the instance variable declaration to which the property<> keyword was attached. This method will be called once for each time the property<> keyword appears. The body of the method makes a simple call to the FieldNode to change its access from public to private.

Running ELIDE with a transform

Once you've written a transformation method for it, itroducing a new keyword into ELIDE is actually a three step process:
  1. Run ELIDE on the Transform java file (eg, Property.java)
  2. Compile the resulting files
  3. Run ELIDE on the target java files (eg, ElideTest.java)
Once you are happy with a particular Transform you can skip the first two steps. Until then, you'll need something like the following three commands, in sequence:

> java -jar elide.jar Property.java -d working

The -d flag specifies which directory to output the new source files into; you should use "working" for Transform files.

> javac -classpath elide.jar working/tutorial/*.java 

Since we specified the "tutorial" package for Property.java, the processed file will be placed in the tutorial directory within working/.

> java -jar elide.jar ElideTest.java -P tutorial

The -P flag specifies a package of Transform subclasses to use; when it hits a keyword such as property<>, ELIDE will look for a matching class in this package. The -P flag can be used multiple times for one run if you have multiple Transform packages.

For ease of development, an automated build tool such as GNU make or Ant is highly recommended (an Ant task for ELIDE is provided in elide.jar; see the ANT.txt file), so that these three commands can all be run at once. Try them out now. This time, the file generated in output should have a subtle change: the instance variables that were marked public in the original file are now marked private.

Extending a class

A single keyword often has multiple effects, and so needs multiple transformation methods. Add another method to the Property class, with the following skeleton:

satisfies<methodDefinition>
public void addAccessors(FieldNode target)
{
}

We want this method to generate new accessor methods for the field it is given. To do so, first we will need to get a representation of the class in which the field was defined, like so:

ClassNode targetClass = target.getDeclaringClass();

Next we will use the extend() method of ClassNode. This allows new members to be added to the class simply by specifying their source code. extend() takes a simple String, but we will create the String using a special string literal syntax introduced in ELIDE: instead of quotes, strings are enclosed between %{ and }%. These strings have three special properties:
  • they can span multiple lines
  • special characters such as " do not need to be escaped
  • they can leave "holes" for variables or expressions using #{ }# to mark off code.
For example,

String text = %{ this is "test" number #{x + 5}#! }%;

is equivalent to

String text = " this is \"test\" number " + (x + 5) + "!";

Add the following code to your class:

String name = target.getName();
String type = target.getType();
String upperCaseName = Utils.methodCase(name);
targetClass.extend(%{
	public #{type}# get#{upperCaseName}#()
	{
		return #{name}#;
	}

	public void set#{upperCaseName}# (#{type}# #{name}#)
	{
		this.#{name}# = #{name}#;
	}
}%);

This adds getter and setter methods to the target class, leaving three holes to be filled in by the name or type of the target instance variable.

The Utils class provides a number of convenience methods such as methodCase (which capitalizes the first letter of a string) - for a full reference, see the Transformation Definition Reference

Your class should currently look like this:


package tutorial;
import ca.ubc.cs.elide.*;
import ca.ubc.cs.elide.nodes.*;

public class Property extends Transform
{	
	satisfies<fieldAccess>
	public void makeFieldPrivate(FieldNode target)
	{
		target.makePrivate();
	}	

	
	satisfies<methodDefinition>
	public void addAccessors(FieldNode target)
	{
		ClassNode targetClass = target.getDeclaringClass();
		String name = target.getName();
		String type = target.getType();
		String upperCaseName = Utils.methodCase(name);            
		targetClass.extend(%{
		        public #{type}# get#{upperCaseName}#()
			{
                		return #{name}#;
        		}
        
       		 	public void set#{upperCaseName}# (#{type}# #{name}#)
        		{
                		this.#{name}# = #{name}#;
        		}
		}%);  
	}
}

And if you now run ELIDE, you should get the following in output/ElideTest.java:

public  class ElideTest extends Object
{
	private  int x;
	private  String[] y;
	
	public int getX()
	{
		return x; 

	}

	public void setX(int x)
	{
		this.x = x;

	}

	public String[] getY()
	{
		return y; 

	}
	
	public void setY(String[] y)
	{
		this.y = y;
	}	
}

Controlling transformations

To conform to the JavaBeans spec, properties must be in a class that implements the Serializable interface. It is a simple matter to add Serializable to the list of interfaces implement by the target class; however, we only wish to do this once, no matter how many properties the class has. ELIDE provides a once<> construct for exactly this purpose. Add the following method to Property.java:

satisfies<interfaceDeclaration>
public void implementSerializable(FieldNode target)
{
	ClassNode targetClass = target.getDeclaringClass();

	once<targetClass, "implement serializable">
	{
		targetClass.add(new TypeNode("Serializable"));
	}
}

The once<> keyword only executes the code within its block a single time for the set of parameters passed to it, so for any given ClassNode, Serializable will only be added the first time this method is called.

Finishing up

This tutorial doesn't implement anything like the full JavaBeans spec. A more complete implementation is available in the examples directory, along with a number of other useful and interesting transformations. Trying to understand those examples is an excellent way to learn ELIDE. The JavaDoc for the nodes hierarchy in the html directory, and also contains some small examples and tips. Finally, questions can be addressed to abryant@cs.ubc.ca or catton@cs.ubc.ca.