Key

This line was added.

This line was removed.

Formatting was changed.

...

This DSLD handles the @Delegate AST transform through a call to delegatesTo.

Code Block

import org.codehaus.groovy.ast.FieldNode
// fields is bound to a single FieldNode (if there is one delegate in the target type),
// or a list of FieldNodes if there are multiple.
currentType(fields : findFieldfields(annotatedBy(Delegate))).accept {
if (fields instanceof Collection) {
provider = "Delegate AST Transform"
// fields is a collection of FieldNodes
for (field in fields) {
delegatesTo(field.declaringTypetype)
}
}
else if (fields instanceof FieldNode) {
delegatesTo fields
}
}

Note that we could have used bind instead of currentType and had the same behavior.

Singleton

This shows how @Singleton is implemented. We need to explicitly use a bind pointcut here since we want to bind to the outermost pointcut.

SwingBuilder

Although this next example is quite long, it is fairly straight forward. We are just adding lots and lots and lots of methods to the SwingBuilder class. This script is only partially complete since we don't include documentation for all contributed methods. It might be possible to use BeanInfo to generate this script automatically.

import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.expr.MapEntryExpression
/**
* This is the meta DSL descriptor for *.dsld files.
*/
// first thing to do is to check that versions are correct
// for this script to be evaluated any further, all conditions must be met
// also supports grailsTooling and sts, which are synonyms and correspond to the STS version eg- 2.6.0
// we can add more version checking on request
supportsVersion(groovy:"1.7.8",groovyEclipse:"2.1.3")
interface IPointcut {
/**
* Accepts a contribution group to this Pointcut. What this means
* is that whenever this pointcut evaluates to a match, then the
* contribution group is evaluated with the bindings that have
* been generated from the pointcut match
*
* @param c the contribution group to accept
*/
def accept(Closure c);
}
// You can store shared pointcuts in a variable // This particular pointcut matches all join points that are inside of a
// groovy project and inside a file with a *.dsld extension and are scripts
def dsldFile = nature("org.eclipse.jdt.groovy.core.groovyNature") & fileExtension("dsld") & isScript()
// You can also create a closure around pointcuts and assign them to
// a variable. Note that when using these variables, you must include parens.
def insideContribution = { enclosingCallName("accept") & inClosure() }
// Ensure that the 'accept' method is available for all closures and variables that correspond to pointcuts
(dsldFile & ( ~ insideContribution() ) & ( ~currentType(subType(Script)) )).accept {
delegatesTo IPointcut
}
// here we store all bound names inside of the wormhole so that they can be available later
(dsldFile & enclosingCallDeclaringType(subType(Script)) & (~ enclosingCallName("supportsVersion")) &
( ~ inClosure() ) & bind(var : variableExpressioncurrentIdentifier())).accept {
if (enclosingNode instanceof MapEntryExpression &&
var.contains(((MapEntryExpression) enclosingNode).keyExpression == var)) { def bindings = wormhole.bindings
if (!bindings) { bindings = [ ]
wormhole.bindings = bindings
} var.each { bindings << varit.text }
}
}
// Define the kinds of pointcuts
// note the different ways of calling the two composite pointcuts
// Also, be careful to use parens around negation '~' since operator precedence may make the '~' apply to the call to 'accept'
(dsldFile & ( ~ insideContribution() ) & currentType(subType(Script)) & (~enclosingCallName("registerPointcut")) &
currentTypeIsEnclosingType()).accept {
provider = "the meta-DSLD script"
// in here, we can list all pointcuts explicitly, or we can access the internal PointcutFactory object
// A little bit naughty, but this is the easiest way to maintain consistency with all possible pointcuts
// ...the PointcutFactory class is a secret class that is declared by groovy-eclipse
// Yes, you can use many Eclipse classes here. The editor won't like them, so they must be fully qualified
// and you cannot import them or use them as types for variables
Map<String, String> pointcutNames = org.codehaus.groovy.eclipse.dsl.script.PointcutFactory.docRegistry
for (pointcutName in pointcutNames.entrySet()) {
method name : pointcutName.key, type: IPointcut, params : [pointcutArg : Object], doc : pointcutName.value
}
method name : "supportsVersion", type : void, params : [versionKindToName : Map.class ], doc : '''Specifies that this script is only active when the specified version constraints are met.
Currently, only 3 constraints are available: groovy, groovyEclipse, sts, and grailsTooling.<br><br>
Use like this:
<pre>supportsVersion(groovy:"1.7.10",groovyEclipse:"2.1.3")</pre>
This means that the current scipt requires <b>both</b> Groovy 1.7.10 or later and Groovy-Eclipse 2.1.3 or later'''
method name : "registerPointcut", type : void, params : [name : String, pointcutBody : Closure], doc : '''
Registers a custom pointcut. This pointcut is only available from within the current script.
You must specify a name for the pointcut as well as a closure that evaluates whether or not there is a match'''
}
// Here, specify everything that can be used inside of an accept block (also called a Contribution Group)
(dsldFile & insideContribution()).accept {
provider = "the meta-DSLD script"
property name : "provider", type : String, doc : """Specifies a <em>Provider</em> for the current contribution.
This provider is displayed during content assist and in other places to give a quick hint as to where this
contribution elementcomes from."""
method name : "property",
useNamedArgs : true,
params : [name : String,
type : Object,
isStatic : boolean,
declaringType : Object,
provider: String,
doc : String],
doc : """
Specifies a new property contribution. <b>name</b> is mandatory, but all other parameters are optional.
"""
method name : "method",
useNamedArgs : true,
params : [name : String,
type : Object,
params : Map,
useNamedArgs : boolean,
isStatic : boolean,
declaringType : Object,
provider: String,
doc : String],
doc : """
Specifies a new method contribution. <b>name</b> is mandatory, but all other parameters are optional.
"""
method name : "delegatesTo", params : [ type : Object ], doc : """
Specify that the currentType delegates tp the given type. The currentType is the type being analyzed.
And the given type is specified as a parameter (either a String, Class, or ClassNode). All fields and
methods of the given type will be available from the currentType.
"""
method name : "delegatesToUseNamedArgs", params : [ type : Object ], doc : """
Specify that the currentType delegates tp the given type. The currentType is the type being analyzed.
And the given type is specified as a parameter (either a String, Class, or ClassNode). All fields and
methods of the given type will be available from the currentType.<br/><br/>
Named arguments will be used for all methods.
"""
property name : "wormhole", type : Map, doc : """Use the wormhole to stuff in values calculated in one contribution group
to make it available later in another contribution group"""
property name : "currentType", type : ClassNode,
doc : "This is the declaring type of the current expression being evaluated."
property name : "currentNode", type : ASTNode, doc : "This is the ASTNode being evaluated"
property name : "enclosingNode", type : ASTNode, doc : "This is the ASTNode enclosing the ASTNode being evaluated"
// Now, extract all bindings from the wormhole and add them as contributions
for (binding in wormhole.bindings) {
property name : binding, doc : "Binding created from pointcut"
}
}
// Add the contributions for inside a "registerPointcut" call. User-registered pointcuts are
// not fully fleshed out yet, so best not to use them yet.
(dsldFile & inClosure() & enclosingCallName("registerPointcut")).accept {
property name : "currentScope", type : org.eclipse.jdt.groovy.search.VariableScope
property name : "currenType", type : ClassNode
}