Last active
December 11, 2016 09:19
-
-
Save Ziphil/840bc6c69ccdb7d5bdb58b3dd3f8a709 to your computer and use it in GitHub Desktop.
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
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 | |
} | |
} |
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
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() | |
} |
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
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