Skip to content

Instantly share code, notes, and snippets.

@Ziphil
Last active December 11, 2016 09:19
Show Gist options
  • Save Ziphil/840bc6c69ccdb7d5bdb58b3dd3f8a709 to your computer and use it in GitHub Desktop.
Save Ziphil/840bc6c69ccdb7d5bdb58b3dd3f8a709 to your computer and use it in GitHub Desktop.
package ziphil.transform
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.ClassCodeExpressionTransformer
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.DynamicVariable
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.InnerClassNode
import org.codehaus.groovy.ast.expr.ClosureExpression
import org.codehaus.groovy.ast.expr.ConstructorCallExpression
import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.control.SourceUnit
@CompileStatic @Newify
public class FieldTransformer extends ClassCodeExpressionTransformer {
private ClassNode clazz
private SourceUnit sourceUnit
private boolean checksOuterClass
public FieldTransformer(ClassNode clazz, SourceUnit sourceUnit, boolean checksOuterClass) {
this.clazz = clazz
this.sourceUnit = sourceUnit
this.checksOuterClass = checksOuterClass
}
public Expression transform(Expression expression) {
if (expression != null) {
if (expression instanceof VariableExpression) {
ClassNode scannedClass = clazz.redirect()
outer: while (true) {
for (FieldNode field : scannedClass.getFields()) {
if (expression.getText() == field.getName() && expression.getAccessedVariable() instanceof DynamicVariable) {
expression.setAccessedVariable(field)
break outer
}
}
if (checksOuterClass && scannedClass instanceof InnerClassNode) {
scannedClass = scannedClass.getOuterClass().redirect()
} else {
break
}
}
} else if (expression instanceof ClosureExpression) {
expression.getCode().visit(this)
} else if (expression instanceof ConstructorCallExpression) {
if (expression.isUsingAnonymousInnerClass()) {
expression.getType().visitContents(this)
}
}
return expression.transformExpression(this)
} else {
return null
}
}
public SourceUnit getSourceUnit() {
return this.sourceUnit
}
}
package ziphil.transform
import groovy.transform.CompileStatic
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import org.codehaus.groovy.transform.GroovyASTTransformationClass
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.TYPE])
@GroovyASTTransformationClass(["ziphil.transform.PartialTransformation"])
@CompileStatic
public @interface Partial {
public Class value()
}
package ziphil.transform
import groovy.transform.CompileStatic
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.AnnotationNode
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.expr.ClassExpression
import org.codehaus.groovy.ast.expr.Expression
import org.codehaus.groovy.ast.stmt.Statement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
@CompileStatic
public class PartialTransformation implements ASTTransformation {
private ClassNode sourceClass
private ClassNode targetClass
private SourceUnit sourceUnit
public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
AnnotationNode annotation = (AnnotationNode)nodes[0]
AnnotatedNode parent = (AnnotatedNode)nodes[1]
this.sourceUnit = sourceUnit
if (parent instanceof ClassNode) {
Expression rawTargetClass = annotation.getMember("value")
if (rawTargetClass instanceof ClassExpression) {
targetClass = rawTargetClass.getType()
sourceClass = (ClassNode)parent
convert()
}
}
}
private void convert() {
convertTargetConstructors()
convertTargetMethods()
transferConstructors()
transferMethods()
transferFields()
}
private void convertTargetConstructors() {
FieldTransformer transformer = new FieldTransformer(sourceClass, sourceUnit, false)
for (ConstructorNode constructor : targetClass.getDeclaredConstructors()) {
transformer.visitConstructor(constructor)
}
}
private void convertTargetMethods() {
FieldTransformer transformer = new FieldTransformer(sourceClass, sourceUnit, false)
for (MethodNode method : targetClass.getMethods()) {
transformer.visitMethod(method)
}
}
private void transferConstructors() {
FieldTransformer transformer = new FieldTransformer(targetClass, sourceUnit, true)
List<ConstructorNode> constructors = new ArrayList(sourceClass.getDeclaredConstructors())
for (ConstructorNode constructor : constructors) {
int modifiers = constructor.getModifiers()
Parameter[] parameters = constructor.getParameters()
ClassNode[] exceptions = constructor.getExceptions()
Statement code = constructor.getCode()
ConstructorNode copiedConstructor = new ConstructorNode(modifiers, parameters, exceptions, code)
copiedConstructor.setSourcePosition(constructor)
transformer.visitConstructor(copiedConstructor)
targetClass.redirect().addConstructor(copiedConstructor)
sourceClass.removeConstructor(constructor)
}
}
private void transferMethods() {
FieldTransformer transformer = new FieldTransformer(targetClass, sourceUnit, true)
List<MethodNode> methods = new ArrayList(sourceClass.getMethods())
for (MethodNode method : methods) {
String name = method.getName()
int modifiers = method.getModifiers()
ClassNode returnType = method.getReturnType()
Parameter[] parameters = method.getParameters()
ClassNode[] exceptions = method.getExceptions()
Statement code = method.getCode()
MethodNode copiedMethod = new MethodNode(name, modifiers, returnType, parameters, exceptions, code)
copiedMethod.setSourcePosition(method)
transformer.visitMethod(copiedMethod)
targetClass.redirect().addMethod(copiedMethod)
sourceClass.removeMethod(method)
}
}
private void transferFields() {
List<FieldNode> fields = new ArrayList(sourceClass.getFields())
for (FieldNode field : fields) {
String name = field.getName()
int modifiers = field.getModifiers()
ClassNode type = field.getType()
ClassNode owner = targetClass
Expression initialValue = field.getInitialValueExpression()
FieldNode copiedField = new FieldNode(name, modifiers, type, owner, initialValue)
copiedField.setSourcePosition(field)
targetClass.addField(copiedField)
sourceClass.removeField(field.getName())
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment