XAspects: Extensible Plug-ins for
Aspect-Oriented Programming
Design Specifications - Interface

Macneil Shonle
Ankit Shah
Version 1.1
12 March 2003

Classes CompilationEnvironment and AspectPlugin form the standard interface as used by any plugin. The design of each of these classes is described below:

CompilationEnvironment Class

Download Source Code of CompilationEnvironment Class

public class CompilationEnvironment {

    private MessageLog errors = new MessageLog(); 
    private MessageLog warnings = new MessageLog();
    private AspectIDRegistry aReg = new AspectIDRegistry();

    // methods that a plug-in can call:
    public void reportError(String mesg, String fromAspectID, int line);
    public void reportError(String mesg, String fromAspectID)
    public void reportError(String mesg)

    public void reportWarning(String mesg, String fromAspectID, int line);
    public void reportWarning(String mesg, String fromAspectID);
    public void reportWarning(String mesg);

    // methods called by ajc:
    public void mapAspect(String aspectID, File file, int startingLine)

    public Iterator getErrors(String fromAspectID);
    public Iterator getWarnings(String fromAspectID);
    public Iterator getErrors()
    public Iterator getWarnings()


    public int getNumErrors(String fromAspectID);
    public int getNumWarnings(String AspectID);
    public int getNumErrors()
    public int getNumWarnings()
}

The 3 data members hold the necessary information. AspectIDRegistry and MessageLog are 2 implementation classes to support the storage of aspect line and filename information and error and warning messages.
AJC Side Methods

mapAspect() method must be called by ajc to register aspect information in the Environment. Failure to do so, will result in the failure of all other methods.
getNumErrors() & getNumWarnings() methods provide information about the total number of errors and warnings generated so far. If an optional aspectID is provided, then only the count of errors or warnings generated for those aspects is returned.
getErrors() & getWarnings() methods return an Iterator to iterate through all the errors or warnings. If no errors or warnings are generated, null is returned. Optional AspectID may passed to get that aspect-specific information.

Plugin Side Methods
reportError() & reportWarning() methods are used by the plugin to report errors and warnings in its operations. This is the only way in which not only compiler errors but also other system errors (absence of some libraries or IO Failure) or any other interruptions to normal execution of the plugin may be reported to the main compiler.
Error / Warning message has to be provided; however, aspectID and line numbers are optional depending on the kind of error / warning
This method will recalculate the actual line number for that aspect with reference to the file it is in before storing the data into the appropriate data element.
class MessageLog {
    private HashMap log = new HashMap();

    public void addMessage(String aspectID, String msg);

    public Iterator extractMessages (String aspectID);
    public Iterator extractMessages ();

    public int size (String fromAspectID);
    public int size ();
}
MessageLog class provides a repository to store errors and warnings generated during compilation. This class is just a support class for CompilationEnvironment and is not visible outside the "xaspects" package
class AspectIDRegistry {
    private HashMap registry = new HashMap();
    class FileInfo {
	File filename;
	int startLine;

	FileInfo (File filename, int startLine);
    }

    void register(String aspectID, File filename, int startLine);
    boolean isRegistered(String aspectID);

    File getFile (String aspectID);
    int getStartLine (String aspectID);

    Iterator allAspectIDs ();
}
AspectIDRegistry stores for an aspect, an ID, a reference to the file it is written in and the line number inside the file where it starts. To store the latter 2 pieces of information, an inner class FileInfo is used. Methods to store and retrieve data are provided.
Like MessageLog, this class is visible only inside the package.

AspectPlugin Class

Download Source Code of AspectPlugin Class



public abstract class AspectPlugin {
    CompilationEnvironment ce;
    private File workingDirectory = null;

    public void init(CompilationEnvironment ce);

    // These must be implemented by Concrete Classes
    abstract public void receiveBody(AspectInfo aspectInfo, String aspectID, String body); 
    abstract public File[] generateExternalInterfaces();
    abstract public File[] generateCode(File[] classFiles);

    public File getWorkingDirectory ();

    public void cleanup();
}

AspectInfo - Supportive Class

Download Source Code of AspectInfo Class

class AspectInfo {
    String packageName = null;
    String[] imports = null;
    String[] modifiers;
    String[] extendings;
    String[] implementings;
    String pluginName;
    String aspectName;
    String body;
    File file;
    int line;

    public AspectInfo(List modifiers, List extendings, List implementings,
                      String pluginName, String aspectName, String body, 
		      File file, int line);

    /* Returns a String representation of the AspectInfo. */
    public String toString();

    /* Set import and package information */
    public void setImports(List imports);
    public void setPackage(String packageName);

    /* "Formatted String" Methods */

    public String getSourceContext();
    public String getModifierString();
    public String getInheritString();

    /* "Direct Access" Methods */

    public String getPackageName ();
    public Iterator getImports();
    public Iterator getModifiers();
    public Iterator getExtends();
    public Iterator getImplements();
    public String getPluginName();
    public String getAspectID();
    public String getBody();
    public int getStartingLine ();
    public File getFile ();
}

AspectPlugin is the superclass of all plugins. Every plugin must inherit from AspectPlugin.
Every Plugin must be initialized to provide the reference to Compilation Environment object.

The 3 abstract methods must be implemented in the plugin. These are the means of communication with the main compiler.
receiveBody() method is called by the compiler to pass all the data specific to an aspect to the plugin for accurate code generation. While in the initial design, this was a trivial method, this one now really does a lot more than just code transfer. We can dissect an aspect input source file as follows:

1
package edu.neu.ccs.xaspects;

2
import java.util.ArrayList;
import java.io.IOException;

3
public abstract    4 aspect (5AspectJ)6ABC7extends HashMap 8implements Iterator 9 {
    ... body ...
}

Now these various pieces of information are passed through the 3 parameters as follows:
Tag     Is                                   is passed thru               in this example
-----------------------------------------------------------------------------------------
1       Name of Package to which             AspectInfo.packageName       packageName="edu.neu.ccs.xaspects"
        this class belongs

2       Packages / Classes Imported          AspectInfo.imports           imports[0]="java.util.ArrayList"
                                                                          imports[1]="java.io.IOException"

3       Modifiers - Space separated list     AspectInfo.modifiers         modifiers[0]="public"
        of                                                                modifiers[1]="abstract"

4       Keyword "aspect". This is not passed

5       Name of Plugin                       should be the name of        class AspectJ extends AspectPlugin
                                             the class that extends
                                             AspectPlugin

6       Name of the aspect                   aspectID                     aspectID="ABC"

7       The class it extends                 AspectInfo.extnds            extnds=HashMap

8       The interfaces it implements         AspectInfo.implmnts          implmnts[0]="Iterator"

9       the body of the class.               body. (However the           body="\n\t... body...\n"
                                             outermost pair of
                                             {} is not included
As you can see, all information concerning the namespace and inheritance environment of the aspect is passed using AspectInfo class. Methods of AspectInfo class are explained below. If no information is to be passed using AspectInfo, all fields of aspectInfo will be null!! [aka new AspectInfo(null, null, null. null, null)]
If certain details in AspectInfo are not available, the corresponding fields will be null.
Body will be "" if nothing to pass.
AspectID cannot be null !

generateExternalInterfaces() method is called by the main compiler to invoke the first pass of compilation to generate interfaces or stubs that should be available for generating reflective information. An array of File objects is returned which refers to the files generated in that pass.If due to error, no files are generated, null must be returned.

generateCode() method is called by the main compiler to invoke the second pass of compilation to generate final set of files. A list of generated class files of interfaces/stubs from the first pass is passed to these methods for taking advantage of structure information. Like the above, it should return an array of File objects or null if no files are found.

Files returned in first pass are not retained by the compiler. If they are needed for the second pass again, they SHOULD be returned again. Retaining the files between passes is the responsibility of the plugin.

getWorkingDirectory() returns a reference to a File object that refers to a temporary directory for that plugin.This directory is then the working directory for that plugin. This method will check to see if a working Directory already exists. If it does, it is returned straightaway. If it doesn't exist, one is created and returned.
This method abstracts the process of having a workingDirectory from the concrete plugins. This method also ensures that no 2 plugins get the same directory as Directories have same name as the plugin class name.

cleanup() is called at the end of compilation by the main compiler to delete all the files generated during the process and also delete the temporary directory. Plugin need not bother about retaining the files. If needed, they will be retained by the main compiler by copying it to some other directory.

Methods of AspectInfo Class

Constructor

Initialize the object with most info about the aspect

Set Methods

These methods are used to set the information about package to which the current aspect belongs and the packages and / or classes imported

Formatted String Methods
These methods return the information in a formatted string that can be used directly without further processing

getSourceContext() returns string that says for the above example:
           "package edu.neu.ccs.xaspects;
            import java.util.ArrayList;
            import java.io.IOException;"
will be returned

getModifierString() returns a String of all modifiers separated by spaces. In above example, the string returned is:
            "public abstract"

getInheritString() returns string that returns a string for extends and implements clauses. For above example:
            "extends HashMap implements Iterator"
will be returned

In each of the above methods, if no data exists, empty strings are returned.

Direct Access Methods
These methods return pieces of information as is. If information is only a string, the String is returned. If it is an array of Strings, an iterator is returned. Null if no data exists.

References

XAspects Project Home Page, <http://www.ccs.neu.edu/research/demeter/xaspects>.

Author: Ankit Shah. Copyright © 2003. All rights reserved.