Dynamic Implementation Tutorial

This tutorial explains how dynamically implement the javax.swing.tree.TreeNode interface for the objects created by the Java source analyzer. The super type of these objects is nice.StatementInfo. The steps of the tutorial are :

  1. Get familiar with the StatementInfo class.
  2. Get familiar with the TreeNode interface.
  3. Create the DI_StatementInfo__TreeNode class.
  4. Create the application which integrates the tree viewer and the Java source analyzer.
  5. Run the application.

 

Get familiar with the StatementInfo class

This is the main class of the Java source analyzer, it has a static analyze() method which receives the name of a Java source file and returns the root object of a tree. The tree represents the structure of the program contained in the source file.

For example the following source file :

import a.b.c.X ;

public class Y
{
public Y (int i)
{
if (i < 0)
{
i_ = 0 ;
}
else
{
i_ = i;
}
}
int i_ ;
}

Generates the following tree :

The objects contained in the tree are all subclasses of StatementInfo. The StatementInfo class has methods to :

You can find the source code in StatementInfo.java and its documentation generated by javadoc in StatementInfo.html.

 

Get familiar with the TreeNode interface

This is the standard javax.swing.tree.TreeNode interface used by the tree facilities of the swing library.

You can find the documentation for this interface in http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/tree/TreeNode.html.

 

Create the DI_StatementInfo__TreeNode class

This is the main step of the tutorial. It is actually the only step required by DJBinder to dynamically implement an interface. After creating this class the StatementInfo objects will behave as if they implement the TreeNode interface.

The first step is to specify the same package of the StatementInfo class :

package nice ;

Then you add all the imports needed. It is not a must, but very likely you may want to import the TreeNode interface.

import javax.swing.tree.TreeNode ;
import java.util.Enumeration ;
import java.util.NoSuchElementException ;

Now you code the header of the class :

public abstract class DI_StatementInfo__TreeNode implements TreeNode

All the dynamic implementation classes must be public and abstract. The name must start by DI_ followed by the base name of the main class, two underscores and finally the base name of the interface. In our case it means "DI_"+"StatementInfo"+"__"+"TreeNode".

And of course this class must implement the TreeNode interface.

This class has one member field :

protected StatementInfo si_ ;

The si_ field refers to the StatementInfo object associated to this instance. In other words the creation of this instance was triggered by the casting of that object into TreeNode. This field is initialized in the init method. This method has to be public and have exactly the following signature :

public void init (Object params)
{
si_ = (StatementInfo)(Object)this ;
}

The expression (StatementInfo)(Object)this; is the standard way used within dynamic implementations to access the associated main object. In other words the this reference is casted into the type of the main object. Java forbids the direct cast from DI_StatementInfo__TreeNode into StatementInfo, that is why a middle cast into Object is used.

In the other methods of this class the si_ field is used as a shortcut for the expression (StatementInfo)(Object)this.

Now you can implement the methods of the TreeNode interface :

public boolean getAllowsChildren()
{
return (si_ instanceof nice.BlockInfo) ;
}

public TreeNode getChildAt(int childIndex)
{
return (TreeNode) si_.childAt (childIndex) ;
}

public int getChildCount()
{
return si_.numberOfChildren() ;
}

public int getIndex(TreeNode node)
{
return si_.find((StatementInfo) node) ;
}

public TreeNode getParent()
{
return (TreeNode) si_.parent() ;
}

public boolean isLeaf()
{
return (si_.numberOfChildren() == 0) ;
}

public Enumeration children()
{
return this.new Enumerator () ;
}

The last method returns an object that must implement the java.util.Enumeration interface, this is done using an internal class :

private class Enumerator implements Enumeration
{
private int pos_ ;

Enumerator ()
{
pos_ = 0 ;
}

public boolean hasMoreElements()
{
return (pos_ < si_.numberOfChildren()) ;
}

public Object nextElement()
{
try
{
return si_.childAt(pos_) ;
}
catch (ArrayIndexOutOfBoundsException e)
{
throw new NoSuchElementException (e.getMessage()) ;
}
}
}

The toString() method is not directly a method of the TreeNode interface, but it is used by the tree viewer component to obtain the text of each node :

public String toString ()
{
return si_.label() ;
}

The String returned is the label of the associated StatementInfo object.

You can find the source code in DI_StatementInfo__TreeNode.java.

 

Create the application that integrates the tree viewer and the Java source analyzer

Now that the objects generated by the Java source analyzer behave as if they implement the TreeNode interface, you can send them to the tree viewer. Here is the code which integrates both components.

import javax.swing.JFrame;
import javax.swing.tree.TreeNode;
import nice.StatementInfo ;
import cannes.TreeViewer ;

public class TreeAppli
{
public static void main(String[] args)
{
String fileName = args[0] ;
TreeNode root = (TreeNode) StatementInfo.analyze (fileName) ;
if (root != null)
{
JFrame frame = new TreeViewer(root);
frame.setVisible(true);
}
}
}

This application assumes that the first argument is the name of a Java source file, this argument is sent to the Statement.analyze method which reads the file, analyze the Java program and returns the root object of a tree containing the structure of the program.

If the root is not null then it is visualized using the tree viewer.

The type of the root object returned by the analyze() method is StatementInfo which must be casted into TreeNode. Normally this cast should throw an exception, but this is not the case when DJBinder is activated, instead a valid TreeNode reference is returned.

You can find the source code in TreeAppli.java.

 

Run the application

This chapter assumes that you use a standard JDK 1.2 of java.sun.com installed in the c:\jdk1.2 directory under Windows. The equivalent commands for other configurations (Unix or another Java virtual machine) are normally straight forward.

Before running this application you have to make sure that the djbinder.jar file is accessible in the runtime environment and it has all the security authorizations granted. The easiest way of doing this is to install the djbinder.jar file as an extension of the Java virtual machine. You can install the djbinder.jar file as an extension by copying it in the c:\jdk1.2\jre\lib\ext directory. You can find more information in Running applications.

You have to download the tutorialFiles.jar file and unzip it. You can use any zip tool like for example the jar tool.

C:\jdk1.2\bin\jar xvf tutorialFiles.jar

The files used by this tutorial are :

TreeAppli.java
cannes\ TreeViewer.java
nice\StatementInfo.java
nice\DI_StatementInfo__TreeNode.java
Test.java (a sample of source file to analyze and visualize)

You must ignore all the other files.

Compile the Java files in the same directory where you have unziped the tutorial files (no need to compile Test.java):

C:\jdk1.2\bin\javac TreeAppli.java cannes\TreeViewer.java nice\StatementInfo.java nice\DI_StatementInfo__TreeNode.java

Run the application using the following command line :

C:\jdk1.2\bin\java amslib.djbinder.Start TreeAppli Test.java

If you have followed these steps you should see a tree with the statements of the Test.java source file. You can expand and collapse the nodes by clicking on them.

On the contrary if you use the command line :

C:\jdk1.2\bin\java TreeAppli Test.java

You would get a ClassCastException because the root object is casted into the TreeNode interface and DJBinder is not activated.

top of this page


DJBinder downloads tutorials consulting contact us links Amslib