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(Script) )).accept { delegatesTo IPointcut } // here we store all bound names inside of the wormhole so that they can be available later (dsldFile & enclosingCallDeclaringType(Script) & (~ enclosingCallName("supportsVersion")) & ( ~ inClosure() ) & bind(var : variableExpression())).accept { if (enclosingNode instanceof MapEntryExpression && ((MapEntryExpression) enclosingNode).keyExpression == var) { def bindings = wormhole.bindings if (!bindings) { bindings = [ ] wormhole.bindings = bindings } bindings << var.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(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 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.

Use like this:
supportsVersion(groovy:"1.7.10",groovyEclipse:"2.1.3")
This means that the current scipt requires both 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 Provider 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. name 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. name 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.

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 }