NetUI Compiler
Introduction
The NetUI compiler layer consists of annotation processors for the following types of files:
- page flow controllers
- shared flow controllers
- form beans
- JavaServer Faces backing beans
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:
- compiler-core: The bulk of the annotation processing code; has no ties to either apt or XDoclet.
- typesystem: Generic typesystem interfaces; are implemented as wrappers by the apt and XDoclet layers. The rest of the core is built on these interfaces.
- processor: Core code that kicks off annotation processing for various file types.
- grammar: Annotatation rules and custom attribute types to support the Checking phase of annotation processing.
- model: The model that is built up for generated files. Once this is built up, it is used to write out the actual XML files that are generated.
- genmodel: The classes here extend the classes in model, and are also aware of annotations. They are used to create model classes based on annotations in the source files.
- compiler-apt: Thin layer that runs annotation processing from within Sun's apt (or from within an IDE that implements Sun's Mirror interfaces, which are compile-time representations of types, annotations, etc.).
- apt: actual apt
AnnotationProcessorFactory
(s) that use delegatingAnnotationProcessor
(s) to delegate to the processors in the core layer. - typesystem.impl: Mirror-based implementations of the typesystem interfaces from the core layer.
- apt: actual apt
- compiler-xdoclet: Thin layer that runs annotation processing from within XDoclet.
- xdoclet: Actual XDoclet task/subtask to delegate to the core annotation processors.
- typesystem.impl: XDoclet-based implementations of the typesystem interfaces from the core layer.
Details
typesystem in compiler-core
The typesystem looks very much like Sun's Mirror interfaces. It contains the following sets of interfaces:
- declaration: Type declarations and everything underneath. These are the bulk of the interesting elements when processing classes. If you have a
ClassDeclaration
, for instance, you can get access to any annotations on the class, any method/field declarations within the class, etc. - type: These are interfaces for type instance (sometimes called a "type usage"). They are different than type declarations because they represent the use of a type within a declaration. For example, this is a type instance in a field declaration, which would produce a
ClassType
forMyObject
:Contrast this to the declaration forpublic MyObject someField;
MyObject
, which would produce aClassDeclaration
:In general, if you have a type usage (e.g.,public class MyObject { ... }
ClassType
), you can get to its type declaration (e.g.,ClassDeclaration
). - env: This contains base annotation processor environment, which is used to get things like the Messager (for emitting errors and warnings), the Filer (for writing out files), the list of type declarations that are being processed, etc.
processor in compiler-core
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.
grammar in compiler-core
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:
- Declarations for member types (attributes) and nested annotation grammars, provided by
addMemberType
,addMemberGrammar
, andaddMemberArrayGrammar
. - Mutually-exclusive attributes (
getMutuallyExclusiveAttrs
): a set of arrays of attribute names that are mutually-exclusive, like outputFormBean and outputFormBeanType on @Jpf.Forward. - Required attributes (
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. - Attribute dependencies (
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. - Custom checking, in
onBeginCheck
,onCheckMember
, andonEndCheck
.
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
.
genmodel in compiler-core
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
.
Future Directions
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.