Stanson AST Framework
Overview
The Stanson AST framework extends the Groovy AST Framework to selectively provide compile-time type and access checking.
The Stanson AST Framework provides the following:
- Compile-time enforcement of static typing, immutability, and encapsulation
- Ability to apply any Annotation globally at compile time
- A framework for creating “Enforcers” which enforce at compile-time the integrity of a Class’ contract
- A framework for creating “Transformers” which modify the AST to auto-generate code and validate rules within a class
- Ability to nest “Enforcers” and “Transformers”
- Additional AST Transformers
Note: Transformers replicate the functionality of the AST Transformers; however,
with this framework, you can nest Transformers. This is somewhat similar to creating aliases with @AnnotationCollector
but supports unbounded nesting of Annotations and ability to add attributes to the composite annotation.
Documentation: API Reference
Enforcers
Enforcers enforce compile-time checks on Classes which are annotated with the @Enforceable annotation. To create an enforcer:
- Create an Annotation which includes the @Enforceable annotation.
- Specify a Class which implements the Enforcer interface for the value attribute.
For an example, look at StaticTypedEnforcer.java
- Add the Annotation to the Class for which you want compile-time enforcement
- The Enforcer will be invoked by the EnforcerDispatcher during the INSTRUCTION_SELECTION phase of the compiler after
all transformations have completed.
The ClassNode under evaluation will be passed into the enforcing ClassNode for enforcement
- The Enforcer can handle the request directly or
leverage different Strategies to evaluate the Class including the ClassEnforcementEvaluator
Transformers transform the AST for a specified Class. To create a Transformer:
- Create an Annotation which includes the @Transformable annotation.
- Specify a Class which implements the Transformer interface for the value attribute.
For an example, look at ImmutableTransformer.java
- Add the Annotation to the Class for which you want to transform
- The Transformer will be invoked by the TransformerDispatcher during the CANONICALIZATION phase of the compiler. It will be passed
in the ClassNode to transform
- @Encapsulate
- @StaticTyped
- The inverse of
@CompileStatic
and @TypeChecked
. Enforces compile-time checking on the consumers of the annotated class, preventing
calling/accessing undefined methods and properties using strong typing. This allows selective application of strong type
checking.
- @FixedCompilation
- A convenience Annotation adding both
@Encapsulate
and @StaticTyped
to a class.
- @ImmutableEnforcement
- Compile time immutable enforcement, including:
- Prevents ability to create your own setters
- Prevents both the defining class consumers from mutating state
- @Immutable
- Copied from Groovy but with the following changes:
- Allows a Class to have its own defined constructors
- Allows optional subclassing of Immutables
- @ClosedConstructor
- Enforces private access to Constructors
- @EnhancedBuilder
- Adds Immutability support to ExternalStrategy
- Allows customization
- Trimming of String
- @Closed
- Convenience Annotation that comprises:
@ClosedConstructor
, @Immutable
, and @FixedCompilation
- @TypeCheck
- Validates type compatibility of the consumers of the Class
All functionality can be used in conjunction with standard AST Transforms, eg. @Builder
Compiler Options
globalAnnotations=<annotation-class>,<annotation-class>
Add specified set of comma separated Annotations to all classes.
Defaults to empty set
enableAstFramework=true|false
If false, the ast-framework will be disabled and no enforcers, transformers, or global application
of Annotations will occur. Defaults to true.
scanEnforcers=<classpath>
If provided, will scan all included jars for Enforcers and Transformers as specified by the classpath.
Defaults to null
Miscellany
- You can apply compiler options using gradle with the following:
tasks.withType(GroovyCompile).configureEach {
if (it.name == "compileGroovy") {
options.forkOptions.jvmArgs = ["-DglobalAnnotations=com.stansonhealth.ast.fixedcompilation.FixedCompilationcom.stansonhealth.ast.immutable.Immutable"]
}
}