Package org.acm.seguin.refactor

Responsible for storing the software that performs the refactorings.

See:
          Description

Interface Summary
ComplexTransform Base class for a program that reads in an abstract syntax tree, transforms the code, and rewrites the file to disk.
 

Class Summary
AddImportTransform This object revises the import statements in the tree.
DefaultComplexTransform Base class for a program that reads in an abstract syntax tree, transforms the code, and rewrites the file to disk.
EliminatePackageImportVisitor Description of the Class
Refactoring Adds a class that is either a parent or a child of an existing class.
RefactoringFactory Factory for all refactorings
RemoveImportTransform This object revises the import statements in the tree.
TransformAST This is the base class for any algorithm that updates the syntax tree.
 

Exception Summary
RefactoringException Description of the Class
 

Package org.acm.seguin.refactor Description

Responsible for storing the software that performs the refactorings. This package contains the base classes for the refactorings. It also has a number of classes that are used by several types of refactorings. This page briefly describes these types, then moves on to describe how to create a new refactoring.

How to create a new refactoring

This portion of the document describes how to create a new refactoring, or at least the methodology that was used to create the existing refactorings.

The first step is to create a set of source files to test on. This file contains the "clean" version of the file before the refactoring was applied. Then I hand edited the file so that it was the correct result. Then applied the pretty printer to the edited file. (If more than one file is updated by a refactoring, this process is repeated for each file.)

The second step is to create a unit test. To do this, I extended org.acm.seguin.junit.DirSourceTestCase. This takes all the features of the TestCase from junit and adds in the specific directories. Root is the working directory, clean contains the unmodified files, and check contains the correct files. The unit test:

  1. Copies the files from the clean directory to the working directory
  2. Applies the refactoring
  3. Compares the file in the working directory to the correct file
  4. Deletes the file in the working directory

To get the unit test to compile, I create a refactoring class. I call the refactoring class XXXRefactoring, and it extends org.acm.seguin.refactor.Refactoring. The refactoring goes into the appropriate package. At the moment, the kinds of refactoring are:

I quickly create the empty methods so that the refactoring is not abstract, and then everything should compile. I compile, and run the unit tests to see that the junit test fails because the files do not match. (Other types of failures are caused by the unit test being incorrect - so now that is debugged.)

Next I create a number of TransformASTs that perform some unit transformation on the parse tree. I've used these to add or remove a node in the parse tree or replace a name everywhere it occurs in the source tree.

Sometimes a TransformAST hands it's work off to a Visitor object. The org.acm.seguin.parser.ChildrenVisitor is a good one to choose as a base class for this visitor. It already provides the ability to traverse the entire tree. Then all I have to do is overload the specific visit methods for the nodes I'm concerned about.

To learn more about what exactly the source tree looks like, look at the java1_1.jjt file that is included in the code.jar file.

Now we have a bunch of TransformAST objects which might call visitors to do their work. How do these get combined together to update a single file? I would then create a ComplexTransform and add to it each TransformAST object that is necessary to update that file. Create an instance of the ComplexTransform with the getComplexTransform() method of the Refactoring object. (This is done to support undoing the refactoring.)

If the effect of a refactoring requires that perhaps a large number of files be modified, then I use a SummaryVisitor to traverse all the source files that are loaded into memory. A good visitor to extend is the org.acm.seguin.summary.TraversalVisitor. It traverses the summary file.

To learn more about the Summary objects, look at the org.acm.seguin.summary package. The Summary objects store the metadata for all the source files that are loaded. One thing to note about the FileSummary objects is that if they have a file that is equal to null, then the FileSummary and associated types are from the JDK or a 3rd party library. These types cannot be updated by the refactoring tool.

By this point we have a Refactoring object. It applies a ComplexTransform to a single file or uses a TraversalVisitor to apply the ComplexTransform to a series of files. The ComplexTransform applies a number of TransformAST objects to update a single parse tree.

Once the unit test passes, you are done!