Last active
December 18, 2015 03:29
-
-
Save aaronzirbes/5718923 to your computer and use it in GitHub Desktop.
Trying to write an AST transformer to make it easier to add a @GrailsEnum annotation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class GrailsEnumType { | |
String name | |
} | |
@interface GrailsEnumHolder { | |
String value() default 'default value' | |
} | |
@interface Parameter { | |
String name() default 'default name' | |
String value() default 'default value' | |
} | |
@interface Type { | |
String type() default 'default' | |
Parameter[] parameters() | |
} | |
import org.codehaus.groovy.transform.AnnotationCollectorTransform | |
import org.codehaus.groovy.ast.AnnotationNode | |
import org.codehaus.groovy.ast.AnnotatedNode | |
import org.codehaus.groovy.ast.ClassHelper | |
import org.codehaus.groovy.ast.expr.Expression | |
import org.codehaus.groovy.ast.expr.ListExpression | |
import org.codehaus.groovy.ast.expr.AnnotationConstantExpression | |
import org.codehaus.groovy.ast.expr.ConstantExpression | |
import org.codehaus.groovy.control.SourceUnit | |
import org.hibernate.annotations.Parameter | |
import org.hibernate.annotations.Type | |
/** | |
* This builds a hibernate annotation to map the custom Grails style enums | |
* using a custom hibernate mapper via the Type annotation. | |
* | |
* Desired resultant annotation: | |
* <pre> | |
* import com.bloomhealthco.radiant.member.qualifyingevent.CoverageChangeEventType | |
* | |
* class SomeEntity { | |
* @Type( | |
* type = 'com.bloomhealthco.radiant.service.hibernate.GrailsEnumType', | |
* parameters = [ | |
* @Parameter( | |
* name = 'enumClass', | |
* value = 'com.bloomhealthco.radiant.member.qualifyingevent.CoverageChangeEventType' ) ] ) | |
* CoverageChangeEventType coverageChangeEventType | |
* } | |
* </pre> | |
* | |
* using the template in GrailsEnum: | |
* <pre> | |
* @Type( parameters = [ @Parameter(type) ] ) | |
* </pre> | |
*/ | |
class GrailsEnumTypeProcessor extends AnnotationCollectorTransform { | |
List<AnnotationNode> visit(AnnotationNode collector, | |
AnnotationNode usage, | |
AnnotatedNode annotated, | |
SourceUnit src) { | |
// Get the class name of the field being annotated | |
String fieldType = annotated.type.name | |
// Build the new Nodes | |
AnnotationNode typeAnnotation = new AnnotationNode(ClassHelper.make(Type)) | |
AnnotationNode parameterAnnotation = new AnnotationNode(ClassHelper.make(Parameter)) | |
ListExpression parameters = new ListExpression( | |
[ new AnnotationConstantExpression(parameterAnnotation) ] | |
) | |
// Set the attributes | |
typeAnnotation.addMember('type', getConstantValue(GrailsEnumType.name)) | |
typeAnnotation.addMember('parameters', parameters) | |
parameterAnnotation.addMember('name', getConstantValue('enumClass')) | |
parameterAnnotation.addMember('value', getConstantValue(fieldType)) | |
return [ typeAnnotation ] | |
} | |
private Expression getConstantValue(String value) { | |
return new ConstantExpression(value) | |
} | |
} | |
new GroovyShell(this.class.classLoader).evaluate ''' | |
import groovy.transform.AnnotationCollector | |
import org.joda.time.LocalDate | |
@Type(parameters = [ @Parameter ]) | |
@AnnotationCollector(processor = 'GrailsEnumTypeProcessor') | |
@interface GrailsEnum {} | |
class GrailsEnumType { | |
String name | |
} | |
enum SomeType { | |
ONE('One'), | |
ANOTHER('Another') | |
final String id | |
SomeType(String id) { | |
this.id = id | |
} | |
String toString() { id } | |
public static SomeType fromString(String value) { | |
SomeType type = values().find {it.id == value} | |
if (!type && (value != 'null')) { | |
String errorMessage = "No matching '${SomeType.name}' found for '$value'" | |
throw new IllegalArgumentException(errorMessage) | |
} | |
type | |
} | |
} | |
class Thing { | |
String wat | |
@GrailsEnum | |
LocalDate today | |
@GrailsEnum | |
SomeType type | |
} | |
println new Thing() | |
''' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment