The NetUI compiler layer consists of annotation processors for the following types of files:
Annotation processing runs under both apt and XDoclet, although apt (processing true Java 5 annotations) is currently the only supported tool.
The architecture involves several layers:
AnnotationProcessorFactory
(s) that use delegating AnnotationProcessor
(s) to delegate to the processors in the core layer.The typesystem looks very much like Sun's Mirror interfaces. It contains the following sets of interfaces:
ClassDeclaration
, for instance, you can get access to any annotations on the class, any method/field declarations within the class, etc.ClassType
for MyObject
:
public MyObject someField; |
MyObject
, which would produce a ClassDeclaration
:
public class MyObject { ... } |
ClassType
), you can get to its type declaration (e.g., ClassDeclaration
).The main annotation processors here are PageFlowAnnotationProcessor
and FormBeanAnnotationProcessor
. Each one kicks off the appropriate checker for the class declaration that is being processed. PageFlowAnnotationProcessor
chooses the checker (e.g., PageFlowChecker
) based on the class-level annotation (e.g., @Jpf.Controller) and the base class for the one being processed (e.g., PageFlowController).
Each checker (all extend BaseChecker
) is the starting point for checking the class, which mostly consists of delegating to the right annotation grammar(s) for the class, and for each method as appropriate.
The classes here extend AnnotationGrammar
, to provide rules for annotations, and AnnotationMemberType
, to provide rules for attributes (members) in annotations. For example, ActionGrammar
provides rules for the @Jpf.Action annotation, and JavaIdentifierType
provides checking for an annotation attribute that must be a valid Java identifier, like returnAction on @Jpf.Forward.
In general, annotation grammars provide five main things, which are used by the base AnnotationGrammar
class:
addMemberType
, addMemberGrammar
, and addMemberArrayGrammar
.getMutuallyExclusiveAttrs
): a set of arrays of attribute names that are mutually-exclusive, like outputFormBean and outputFormBeanType on @Jpf.Forward.getRequiredAttrs
): a set of arrays, where each array specifies that at least one of a group of attributes is required. For instance at least one of path, navigateTo, returnAction, action, or tilesDefinition is required on @Jpf.Forward.getAttrDependenciese
): a set of arrays that describe attribute dependencies. The first element is the attribute in question, which is only allowed if at least one of the rest of the attributes is present. For example on @Jpf.Forward, the externalRedirect attribute is only allowed if the path is present.onBeginCheck
, onCheckMember
, and onEndCheck
.
Annotation member types simply provide custom checking by overriding onCheck
.
All of the grammar/type checking is kicked off during the check
phase; see BaseAnnotationProcessor
.
During the generate
phase of annotation processing (see BaseAnnotationProcessor
), instances of classes in genmodel are created based on declarations for annotated classes being processed. As an example, see GenStrutsApp
, which accepts a ClassDeclaration
(the annotated page flow or shared flow class) in its constructor. It builds up a model based on the annotations, using its base class StrutsApp
setters. Once the model is built up, it is written to XML using base class methods in StrutsApp
.
As Eddie has suggested, some of this code could move to a Jakarta Commons-type project. This would include the typesystem package in compiler-core, some basic annotation grammar/type classes, and some skeleton code for apt {{AnnotationProcessor}}s and XDoclet tasks from compiler-apt and compiler-xdoclet, respectively.