mirror of
https://github.com/alibaba/testable-mock.git
synced 2025-01-10 20:30:11 +08:00
extract test setup method to standalone generator
This commit is contained in:
parent
fa79075055
commit
35a29aa95c
@ -0,0 +1,112 @@
|
|||||||
|
package com.alibaba.testable.generator;
|
||||||
|
|
||||||
|
import com.alibaba.testable.model.TestLibType;
|
||||||
|
import com.alibaba.testable.model.TestableContext;
|
||||||
|
import com.alibaba.testable.util.ConstPool;
|
||||||
|
import com.sun.tools.javac.code.Type;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.*;
|
||||||
|
import com.sun.tools.javac.util.List;
|
||||||
|
import com.sun.tools.javac.util.ListBuffer;
|
||||||
|
import com.sun.tools.javac.util.Name;
|
||||||
|
import com.sun.tools.javac.util.Pair;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate test class setup method definition
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class TestSetupMethodGenerator {
|
||||||
|
|
||||||
|
private static final String TYPE_CLASS = "Class";
|
||||||
|
private final TestableContext cx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MethodName -> (ResultType -> ParameterTypes)
|
||||||
|
*/
|
||||||
|
public ListBuffer<Pair<Name, Pair<JCExpression, List<JCExpression>>>> injectMethods = new ListBuffer<>();
|
||||||
|
public String testSetupMethodName = "";
|
||||||
|
public TestLibType testLibType = TestLibType.JUnit4;
|
||||||
|
public final ListBuffer<Method> memberMethods = new ListBuffer<>();
|
||||||
|
|
||||||
|
public TestSetupMethodGenerator(TestableContext cx) {
|
||||||
|
this.cx = cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JCMethodDecl fetch() {
|
||||||
|
JCModifiers mods = cx.treeMaker.Modifiers(Modifier.PUBLIC, makeAnnotations(ConstPool.ANNOTATION_JUNIT5_SETUP));
|
||||||
|
return cx.treeMaker.MethodDef(mods, cx.names.fromString("testableSetup"),
|
||||||
|
cx.treeMaker.Type(new Type.JCVoidType()), List.<JCTypeParameter>nil(),
|
||||||
|
List.<JCVariableDecl>nil(), List.<JCExpression>nil(), testableSetupBlock(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<JCAnnotation> makeAnnotations(String fullAnnotationName) {
|
||||||
|
JCExpression setupAnnotation = nameToExpression(fullAnnotationName);
|
||||||
|
return List.of(cx.treeMaker.Annotation(setupAnnotation, List.<JCExpression>nil()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private JCBlock testableSetupBlock() {
|
||||||
|
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
||||||
|
for (Pair<Name, Pair<JCExpression, List<JCExpression>>> m : injectMethods.toList()) {
|
||||||
|
if (isMemberMethod(m)) {
|
||||||
|
statements.append(addToPoolStatement(m, ConstPool.NE_ADD_F));
|
||||||
|
} else {
|
||||||
|
statements.append(addToPoolStatement(m, ConstPool.NE_ADD_W));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!testSetupMethodName.isEmpty()) {
|
||||||
|
statements.append(cx.treeMaker.Exec(cx.treeMaker.Apply(List.<JCExpression>nil(),
|
||||||
|
nameToExpression(testSetupMethodName), List.<JCExpression>nil())));
|
||||||
|
}
|
||||||
|
return cx.treeMaker.Block(0, statements.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JCExpression nameToExpression(String dotName) {
|
||||||
|
String[] nameParts = dotName.split("\\.");
|
||||||
|
JCExpression e = cx.treeMaker.Ident(cx.names.fromString(nameParts[0]));
|
||||||
|
for (int i = 1; i < nameParts.length; i++) {
|
||||||
|
e = cx.treeMaker.Select(e, cx.names.fromString(nameParts[i]));
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMemberMethod(Pair<Name, Pair<JCExpression, List<JCExpression>>> m) {
|
||||||
|
for (Method method : memberMethods) {
|
||||||
|
if (method.getName().equals(m.fst.toString()) && parameterEquals(m.snd.snd, method.getParameterTypes())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean parameterEquals(List<JCExpression> injectMethodArgs, Class<?>[] memberMethodArgs) {
|
||||||
|
if (injectMethodArgs.length() != memberMethodArgs.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < injectMethodArgs.length(); i++) {
|
||||||
|
if (!memberMethodArgs[i].getName().equals(((JCFieldAccess)injectMethodArgs.get(i)).selected.type
|
||||||
|
.toString())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JCStatement addToPoolStatement(Pair<Name, Pair<JCExpression, List<JCExpression>>> m, String addPoolMethod) {
|
||||||
|
JCExpression pool = nameToExpression(ConstPool.NE_POOL);
|
||||||
|
JCExpression classType = m.snd.fst;
|
||||||
|
JCExpression methodName = cx.treeMaker.Literal(m.fst.toString());
|
||||||
|
JCExpression parameterTypes = cx.treeMaker.NewArray(cx.treeMaker.Ident(cx.names.fromString(TYPE_CLASS)),
|
||||||
|
List.<JCExpression>nil(), m.snd.snd);
|
||||||
|
JCExpression thisIns = cx.treeMaker.Ident(cx.names.fromString(ConstPool.REF_THIS));
|
||||||
|
JCNewClass poolClass = cx.treeMaker.NewClass(null, List.<JCExpression>nil(), pool,
|
||||||
|
List.of(classType, methodName, parameterTypes, thisIns), null);
|
||||||
|
JCExpression addInjectMethod = nameToExpression(addPoolMethod);
|
||||||
|
JCMethodInvocation apply = cx.treeMaker.Apply(List.<JCExpression>nil(), addInjectMethod,
|
||||||
|
List.from(new JCExpression[] {poolClass}));
|
||||||
|
return cx.treeMaker.Exec(apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,7 @@ package com.alibaba.testable.generator;
|
|||||||
import com.alibaba.testable.generator.model.Statement;
|
import com.alibaba.testable.generator.model.Statement;
|
||||||
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
|
import com.alibaba.testable.generator.statement.CallSuperMethodStatementGenerator;
|
||||||
import com.alibaba.testable.model.TestableContext;
|
import com.alibaba.testable.model.TestableContext;
|
||||||
import com.alibaba.testable.translator.TestableClassDevRoleTranslator;
|
import com.alibaba.testable.translator.EnableTestableInjectTranslator;
|
||||||
import com.alibaba.testable.util.ConstPool;
|
import com.alibaba.testable.util.ConstPool;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
@ -23,17 +23,17 @@ import java.util.Set;
|
|||||||
*
|
*
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
public class TestableClassDevRoleGenerator {
|
public class TestableClassGenerator {
|
||||||
|
|
||||||
private final TestableContext cx;
|
private final TestableContext cx;
|
||||||
|
|
||||||
public TestableClassDevRoleGenerator(TestableContext cx) {
|
public TestableClassGenerator(TestableContext cx) {
|
||||||
this.cx = cx;
|
this.cx = cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String fetch(Symbol.ClassSymbol clazz, String packageName, String className) {
|
public String fetch(Symbol.ClassSymbol clazz, String packageName, String className) {
|
||||||
JCTree tree = cx.trees.getTree(clazz);
|
JCTree tree = cx.trees.getTree(clazz);
|
||||||
TestableClassDevRoleTranslator translator = new TestableClassDevRoleTranslator(cx);
|
EnableTestableInjectTranslator translator = new EnableTestableInjectTranslator(cx);
|
||||||
tree.accept(translator);
|
tree.accept(translator);
|
||||||
|
|
||||||
List<MethodSpec> methodSpecs = new ArrayList<>();
|
List<MethodSpec> methodSpecs = new ArrayList<>();
|
@ -2,7 +2,7 @@ package com.alibaba.testable.processor;
|
|||||||
|
|
||||||
import com.alibaba.testable.annotation.EnableTestableInject;
|
import com.alibaba.testable.annotation.EnableTestableInject;
|
||||||
import com.alibaba.testable.generator.StaticNewClassGenerator;
|
import com.alibaba.testable.generator.StaticNewClassGenerator;
|
||||||
import com.alibaba.testable.generator.TestableClassDevRoleGenerator;
|
import com.alibaba.testable.generator.TestableClassGenerator;
|
||||||
import com.alibaba.testable.util.ConstPool;
|
import com.alibaba.testable.util.ConstPool;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ public class EnableTestableInjectProcessor extends BaseProcessor {
|
|||||||
String fullQualityTypeName = packageName + "." + testableTypeName;
|
String fullQualityTypeName = packageName + "." + testableTypeName;
|
||||||
try {
|
try {
|
||||||
writeSourceFile(fullQualityTypeName,
|
writeSourceFile(fullQualityTypeName,
|
||||||
new TestableClassDevRoleGenerator(cx).fetch(clazz, packageName, testableTypeName));
|
new TestableClassGenerator(cx).fetch(clazz, packageName, testableTypeName));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.alibaba.testable.processor;
|
package com.alibaba.testable.processor;
|
||||||
|
|
||||||
import com.alibaba.testable.annotation.EnableTestable;
|
import com.alibaba.testable.annotation.EnableTestable;
|
||||||
import com.alibaba.testable.translator.TestableClassTestRoleTranslator;
|
import com.alibaba.testable.translator.EnableTestableTranslator;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ public class EnableTestableProcessor extends BaseProcessor {
|
|||||||
|
|
||||||
private void processClassElement(Symbol.ClassSymbol clazz) {
|
private void processClassElement(Symbol.ClassSymbol clazz) {
|
||||||
JCTree tree = cx.trees.getTree(clazz);
|
JCTree tree = cx.trees.getTree(clazz);
|
||||||
tree.accept(new TestableClassTestRoleTranslator(getPkgName(clazz), getOriginClassName(clazz), cx));
|
tree.accept(new EnableTestableTranslator(getPkgName(clazz), getOriginClassName(clazz), cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPkgName(Symbol.ClassSymbol clazz) {
|
private String getPkgName(Symbol.ClassSymbol clazz) {
|
||||||
|
@ -14,7 +14,7 @@ import com.sun.tools.javac.util.Name;
|
|||||||
*
|
*
|
||||||
* @author flin
|
* @author flin
|
||||||
*/
|
*/
|
||||||
public class TestableClassDevRoleTranslator extends TreeTranslator {
|
public class EnableTestableInjectTranslator extends TreeTranslator {
|
||||||
|
|
||||||
private final TestableContext cx;
|
private final TestableContext cx;
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestableClassDevRoleTranslator(TestableContext cx) {
|
public EnableTestableInjectTranslator(TestableContext cx) {
|
||||||
this.cx = cx;
|
this.cx = cx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ public class TestableClassDevRoleTranslator extends TreeTranslator {
|
|||||||
private JCTree.JCMethodInvocation getGlobalMemberInvocation(Name methodName, List<JCTree.JCExpression> param) {
|
private JCTree.JCMethodInvocation getGlobalMemberInvocation(Name methodName, List<JCTree.JCExpression> param) {
|
||||||
JCTree.JCFieldAccess snClass = cx.treeMaker.Select(cx.treeMaker.Ident(cx.names.fromString(ConstPool.NE_PKG)),
|
JCTree.JCFieldAccess snClass = cx.treeMaker.Select(cx.treeMaker.Ident(cx.names.fromString(ConstPool.NE_PKG)),
|
||||||
cx.names.fromString(ConstPool.NE_CLS));
|
cx.names.fromString(ConstPool.NE_CLS));
|
||||||
JCTree.JCFieldAccess snMethod = cx.treeMaker.Select(snClass, cx.names.fromString(ConstPool.NE_INK));
|
JCTree.JCFieldAccess snMethod = cx.treeMaker.Select(snClass, cx.names.fromString(ConstPool.NE_FUN));
|
||||||
ListBuffer<JCTree.JCExpression> args = new ListBuffer();
|
ListBuffer<JCTree.JCExpression> args = new ListBuffer();
|
||||||
args.add(cx.treeMaker.Ident(cx.names.fromString(ConstPool.REF_THIS)));
|
args.add(cx.treeMaker.Ident(cx.names.fromString(ConstPool.REF_THIS)));
|
||||||
args.add(cx.treeMaker.Literal(methodName.toString()));
|
args.add(cx.treeMaker.Literal(methodName.toString()));
|
@ -0,0 +1,181 @@
|
|||||||
|
package com.alibaba.testable.translator;
|
||||||
|
|
||||||
|
import com.alibaba.testable.generator.TestSetupMethodGenerator;
|
||||||
|
import com.alibaba.testable.model.TestLibType;
|
||||||
|
import com.alibaba.testable.model.TestableContext;
|
||||||
|
import com.alibaba.testable.util.ConstPool;
|
||||||
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
import com.sun.tools.javac.tree.TreeTranslator;
|
||||||
|
import com.sun.tools.javac.util.*;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Travel AST
|
||||||
|
*
|
||||||
|
* @author flin
|
||||||
|
*/
|
||||||
|
public class EnableTestableTranslator extends TreeTranslator {
|
||||||
|
|
||||||
|
private final TestableContext cx;
|
||||||
|
private String sourceClassName = "";
|
||||||
|
private final ListBuffer<Name> sourceClassIns = new ListBuffer<>();
|
||||||
|
private final ListBuffer<String> stubbornFields = new ListBuffer<>();
|
||||||
|
private final TestSetupMethodGenerator testSetupMethodGenerator;
|
||||||
|
|
||||||
|
public EnableTestableTranslator(String pkgName, String className, TestableContext cx) {
|
||||||
|
this.sourceClassName = className;
|
||||||
|
this.cx = cx;
|
||||||
|
this.testSetupMethodGenerator = new TestSetupMethodGenerator(cx);
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName(pkgName + "." + className);
|
||||||
|
Field[] fields = cls.getDeclaredFields();
|
||||||
|
for (Field f : fields) {
|
||||||
|
if (Modifier.isFinal(f.getModifiers()) || Modifier.isPrivate(f.getModifiers())) {
|
||||||
|
stubbornFields.add(f.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testSetupMethodGenerator.memberMethods.addAll(Arrays.asList(cls.getDeclaredMethods()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo d = new Demo() -> DemoTestable d = new Demo()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitVarDef(JCVariableDecl jcVariableDecl) {
|
||||||
|
super.visitVarDef(jcVariableDecl);
|
||||||
|
if (jcVariableDecl.vartype.getClass().equals(JCIdent.class) &&
|
||||||
|
((JCIdent)jcVariableDecl.vartype).name.toString().equals(sourceClassName)) {
|
||||||
|
jcVariableDecl.vartype = getTestableClassIdent(jcVariableDecl.vartype);
|
||||||
|
sourceClassIns.add(jcVariableDecl.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demo d = new Demo() -> Demo d = new DemoTestable()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitNewClass(JCNewClass jcNewClass) {
|
||||||
|
super.visitNewClass(jcNewClass);
|
||||||
|
if (getSimpleClassName(jcNewClass).equals(sourceClassName)) {
|
||||||
|
jcNewClass.clazz = getTestableClassIdent(jcNewClass.clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* d.privateField = val -> d.privateFieldTestableSet(val)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitExec(JCExpressionStatement jcExpressionStatement) {
|
||||||
|
if (jcExpressionStatement.expr.getClass().equals(JCAssign.class) &&
|
||||||
|
isAssignStubbornField((JCAssign)jcExpressionStatement.expr)) {
|
||||||
|
JCAssign assign = (JCAssign)jcExpressionStatement.expr;
|
||||||
|
JCFieldAccess stubbornSetter = cx.treeMaker.Select(((JCFieldAccess)assign.lhs).selected,
|
||||||
|
getStubbornSetterMethodName(assign));
|
||||||
|
jcExpressionStatement.expr = cx.treeMaker.Apply(List.<JCExpression>nil(), stubbornSetter,
|
||||||
|
com.sun.tools.javac.util.List.of(assign.rhs));
|
||||||
|
}
|
||||||
|
super.visitExec(jcExpressionStatement);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for TestableInject and TestSetup annotations
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitMethodDef(JCMethodDecl jcMethodDecl) {
|
||||||
|
for (JCAnnotation a : jcMethodDecl.mods.annotations) {
|
||||||
|
switch (a.type.tsym.toString()) {
|
||||||
|
case ConstPool.ANNOTATION_TESTABLE_INJECT:
|
||||||
|
ListBuffer<JCExpression> args = new ListBuffer<>();
|
||||||
|
for (JCVariableDecl p : jcMethodDecl.params) {
|
||||||
|
args.add(cx.treeMaker.Select(p.vartype, cx.names.fromString(ConstPool.TYPE_TO_CLASS)));
|
||||||
|
}
|
||||||
|
JCExpression retType = jcMethodDecl.restype == null ? null :
|
||||||
|
cx.treeMaker.Select(jcMethodDecl.restype, cx.names.fromString(ConstPool.TYPE_TO_CLASS));
|
||||||
|
testSetupMethodGenerator.injectMethods.add(Pair.of(jcMethodDecl.name, Pair.of(retType, args.toList())));
|
||||||
|
break;
|
||||||
|
case ConstPool.ANNOTATION_JUNIT5_SETUP:
|
||||||
|
testSetupMethodGenerator.testSetupMethodName = jcMethodDecl.name.toString();
|
||||||
|
jcMethodDecl.mods.annotations = removeAnnotation(jcMethodDecl.mods.annotations,
|
||||||
|
ConstPool.ANNOTATION_JUNIT5_SETUP);
|
||||||
|
break;
|
||||||
|
case ConstPool.ANNOTATION_JUNIT5_TEST:
|
||||||
|
testSetupMethodGenerator.testLibType = TestLibType.JUnit5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitMethodDef(jcMethodDecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate test setup method to initialize n.e.pool
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitClassDef(JCClassDecl jcClassDecl) {
|
||||||
|
super.visitClassDef(jcClassDecl);
|
||||||
|
ListBuffer<JCTree> ndefs = new ListBuffer<>();
|
||||||
|
ndefs.addAll(jcClassDecl.defs);
|
||||||
|
ndefs.add(testSetupMethodGenerator.fetch());
|
||||||
|
jcClassDecl.defs = ndefs.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For setter break point
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitAssign(JCAssign jcAssign) {
|
||||||
|
super.visitAssign(jcAssign);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For getter break point
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void visitSelect(JCFieldAccess jcFieldAccess) {
|
||||||
|
super.visitSelect(jcFieldAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSimpleClassName(JCNewClass jcNewClass) {
|
||||||
|
if (jcNewClass.clazz.getClass().equals(JCIdent.class)) {
|
||||||
|
return ((JCIdent)jcNewClass.clazz).name.toString();
|
||||||
|
} else if (jcNewClass.clazz.getClass().equals(JCFieldAccess.class)) {
|
||||||
|
return ((JCFieldAccess)jcNewClass.clazz).name.toString();
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<JCAnnotation> removeAnnotation(List<JCAnnotation> annotations, String target) {
|
||||||
|
ListBuffer<JCAnnotation> nb = new ListBuffer<>();
|
||||||
|
for (JCAnnotation i : annotations) {
|
||||||
|
if (!i.type.tsym.toString().equals(target)) {
|
||||||
|
nb.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nb.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Name getStubbornSetterMethodName(JCAssign assign) {
|
||||||
|
String name = ((JCFieldAccess)assign.lhs).name.toString() + ConstPool.TESTABLE_SET_METHOD_PREFIX;
|
||||||
|
return cx.names.fromString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAssignStubbornField(JCAssign expr) {
|
||||||
|
return expr.lhs.getClass().equals(JCFieldAccess.class) &&
|
||||||
|
sourceClassIns.contains(((JCIdent)((JCFieldAccess)(expr).lhs).selected).name) &&
|
||||||
|
stubbornFields.contains(((JCFieldAccess)(expr).lhs).name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JCIdent getTestableClassIdent(JCExpression clazz) {
|
||||||
|
Name className = ((JCIdent)clazz).name;
|
||||||
|
return cx.treeMaker.Ident(cx.names.fromString(className + ConstPool.TESTABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,283 +0,0 @@
|
|||||||
package com.alibaba.testable.translator;
|
|
||||||
|
|
||||||
import com.alibaba.testable.model.TestLibType;
|
|
||||||
import com.alibaba.testable.model.TestableContext;
|
|
||||||
import com.alibaba.testable.util.ConstPool;
|
|
||||||
import com.sun.tools.javac.code.Type;
|
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
|
||||||
import com.sun.tools.javac.tree.TreeTranslator;
|
|
||||||
import com.sun.tools.javac.util.*;
|
|
||||||
import com.sun.tools.javac.tree.JCTree.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Travel AST
|
|
||||||
*
|
|
||||||
* @author flin
|
|
||||||
*/
|
|
||||||
public class TestableClassTestRoleTranslator extends TreeTranslator {
|
|
||||||
|
|
||||||
private static final String ANNOTATION_TESTABLE_INJECT = "com.alibaba.testable.annotation.TestableInject";
|
|
||||||
private static final String ANNOTATION_JUNIT5_SETUP = "org.junit.jupiter.api.BeforeEach";
|
|
||||||
private static final String ANNOTATION_JUNIT5_TEST = "org.junit.jupiter.api.Test";
|
|
||||||
private static final String TYPE_CLASS = "Class";
|
|
||||||
private final TestableContext cx;
|
|
||||||
private String sourceClassName = "";
|
|
||||||
private final ListBuffer<Name> sourceClassIns = new ListBuffer();
|
|
||||||
private final ListBuffer<String> stubbornFields = new ListBuffer();
|
|
||||||
private final ListBuffer<Method> memberMethods = new ListBuffer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MethodName -> (ResultType -> ParameterTypes)
|
|
||||||
*/
|
|
||||||
private ListBuffer<Pair<Name, Pair<JCExpression, List<JCExpression>>>> injectMethods = new ListBuffer<>();
|
|
||||||
private String testSetupMethodName = "";
|
|
||||||
private TestLibType testLibType = TestLibType.JUnit4;
|
|
||||||
|
|
||||||
public TestableClassTestRoleTranslator(String pkgName, String className, TestableContext cx) {
|
|
||||||
this.sourceClassName = className;
|
|
||||||
this.cx = cx;
|
|
||||||
try {
|
|
||||||
Class<?> cls = Class.forName(pkgName + "." + className);
|
|
||||||
Field[] fields = cls.getDeclaredFields();
|
|
||||||
for (Field f : fields) {
|
|
||||||
if (Modifier.isFinal(f.getModifiers()) || Modifier.isPrivate(f.getModifiers())) {
|
|
||||||
stubbornFields.add(f.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memberMethods.addAll(Arrays.asList(cls.getDeclaredMethods()));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Demo d = new Demo() -> DemoTestable d = new Demo()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitVarDef(JCVariableDecl jcVariableDecl) {
|
|
||||||
super.visitVarDef(jcVariableDecl);
|
|
||||||
if (jcVariableDecl.vartype.getClass().equals(JCIdent.class) &&
|
|
||||||
((JCIdent)jcVariableDecl.vartype).name.toString().equals(sourceClassName)) {
|
|
||||||
jcVariableDecl.vartype = getTestableClassIdent(jcVariableDecl.vartype);
|
|
||||||
sourceClassIns.add(jcVariableDecl.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Demo d = new Demo() -> Demo d = new DemoTestable()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitNewClass(JCNewClass jcNewClass) {
|
|
||||||
super.visitNewClass(jcNewClass);
|
|
||||||
if (getSimpleClassName(jcNewClass).equals(sourceClassName)) {
|
|
||||||
jcNewClass.clazz = getTestableClassIdent(jcNewClass.clazz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* d.privateField = val -> d.privateFieldTestableSet(val)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitExec(JCExpressionStatement jcExpressionStatement) {
|
|
||||||
if (jcExpressionStatement.expr.getClass().equals(JCAssign.class) &&
|
|
||||||
isAssignStubbornField((JCAssign)jcExpressionStatement.expr)) {
|
|
||||||
JCAssign assign = (JCAssign)jcExpressionStatement.expr;
|
|
||||||
JCFieldAccess stubbornSetter = cx.treeMaker.Select(((JCFieldAccess)assign.lhs).selected,
|
|
||||||
getStubbornSetterMethodName(assign));
|
|
||||||
jcExpressionStatement.expr = cx.treeMaker.Apply(List.<JCExpression>nil(), stubbornSetter,
|
|
||||||
com.sun.tools.javac.util.List.of(assign.rhs));
|
|
||||||
}
|
|
||||||
super.visitExec(jcExpressionStatement);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for TestableInject and TestSetup annotations
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitMethodDef(JCMethodDecl jcMethodDecl) {
|
|
||||||
for (JCAnnotation a : jcMethodDecl.mods.annotations) {
|
|
||||||
switch (a.type.tsym.toString()) {
|
|
||||||
case ANNOTATION_TESTABLE_INJECT:
|
|
||||||
ListBuffer<JCExpression> args = new ListBuffer<>();
|
|
||||||
for (JCVariableDecl p : jcMethodDecl.params) {
|
|
||||||
args.add(cx.treeMaker.Select(p.vartype, cx.names.fromString(ConstPool.TYPE_TO_CLASS)));
|
|
||||||
}
|
|
||||||
JCExpression retType = jcMethodDecl.restype == null ? null :
|
|
||||||
cx.treeMaker.Select(jcMethodDecl.restype, cx.names.fromString(ConstPool.TYPE_TO_CLASS));
|
|
||||||
injectMethods.add(Pair.of(jcMethodDecl.name, Pair.of(retType, args.toList())));
|
|
||||||
break;
|
|
||||||
case ANNOTATION_JUNIT5_SETUP:
|
|
||||||
testSetupMethodName = jcMethodDecl.name.toString();
|
|
||||||
jcMethodDecl.mods.annotations = removeAnnotation(jcMethodDecl.mods.annotations,
|
|
||||||
ANNOTATION_JUNIT5_SETUP);
|
|
||||||
break;
|
|
||||||
case ANNOTATION_JUNIT5_TEST:
|
|
||||||
testLibType = TestLibType.JUnit5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.visitMethodDef(jcMethodDecl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate test setup method to initialize n.e.pool
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitClassDef(JCClassDecl jcClassDecl) {
|
|
||||||
super.visitClassDef(jcClassDecl);
|
|
||||||
ListBuffer<JCTree> ndefs = new ListBuffer<>();
|
|
||||||
ndefs.addAll(jcClassDecl.defs);
|
|
||||||
JCModifiers mods = cx.treeMaker.Modifiers(Modifier.PUBLIC, makeAnnotations(ANNOTATION_JUNIT5_SETUP));
|
|
||||||
ndefs.add(cx.treeMaker.MethodDef(mods, cx.names.fromString("testableSetup"),
|
|
||||||
cx.treeMaker.Type(new Type.JCVoidType()), List.<JCTypeParameter>nil(),
|
|
||||||
List.<JCVariableDecl>nil(), List.<JCExpression>nil(), testableSetupBlock(), null));
|
|
||||||
jcClassDecl.defs = ndefs.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For setter break point
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitAssign(JCAssign jcAssign) {
|
|
||||||
super.visitAssign(jcAssign);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For getter break point
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void visitSelect(JCFieldAccess jcFieldAccess) {
|
|
||||||
super.visitSelect(jcFieldAccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSimpleClassName(JCNewClass jcNewClass) {
|
|
||||||
if (jcNewClass.clazz.getClass().equals(JCIdent.class)) {
|
|
||||||
return ((JCIdent)jcNewClass.clazz).name.toString();
|
|
||||||
} else if (jcNewClass.clazz.getClass().equals(JCFieldAccess.class)) {
|
|
||||||
return ((JCFieldAccess)jcNewClass.clazz).name.toString();
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<JCAnnotation> makeAnnotations(String fullAnnotationName) {
|
|
||||||
JCExpression setupAnnotation = nameToExpression(fullAnnotationName);
|
|
||||||
return List.of(cx.treeMaker.Annotation(setupAnnotation, List.<JCExpression>nil()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCExpression nameToExpression(String dotName) {
|
|
||||||
String[] nameParts = dotName.split("\\.");
|
|
||||||
JCExpression e = cx.treeMaker.Ident(cx.names.fromString(nameParts[0]));
|
|
||||||
for (int i = 1; i < nameParts.length; i++) {
|
|
||||||
e = cx.treeMaker.Select(e, cx.names.fromString(nameParts[i]));
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCBlock testableSetupBlock() {
|
|
||||||
ListBuffer<JCStatement> statements = new ListBuffer<>();
|
|
||||||
for (Pair<Name, Pair<JCExpression, List<JCExpression>>> m : injectMethods.toList()) {
|
|
||||||
if (isMemberMethod(m)) {
|
|
||||||
statements.append(toGlobalInvokeStatement(m));
|
|
||||||
} else {
|
|
||||||
statements.append(toGlobalNewStatement(m));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!testSetupMethodName.isEmpty()) {
|
|
||||||
statements.append(cx.treeMaker.Exec(cx.treeMaker.Apply(List.<JCExpression>nil(),
|
|
||||||
nameToExpression(testSetupMethodName), List.<JCExpression>nil())));
|
|
||||||
}
|
|
||||||
return cx.treeMaker.Block(0, statements.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMemberMethod(Pair<Name, Pair<JCExpression, List<JCExpression>>> m) {
|
|
||||||
for (Method method : memberMethods) {
|
|
||||||
if (method.getName().equals(m.fst.toString()) && parameterEquals(m.snd.snd, method.getParameterTypes())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean parameterEquals(List<JCExpression> injectMethodArgs, Class<?>[] memberMethodArgs) {
|
|
||||||
if (injectMethodArgs.length() != memberMethodArgs.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < injectMethodArgs.length(); i++) {
|
|
||||||
if (!memberMethodArgs[i].getName().equals(((JCFieldAccess)injectMethodArgs.get(i)).selected.type
|
|
||||||
.toString())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCStatement toGlobalInvokeStatement(Pair<Name, Pair<JCExpression, List<JCExpression>>> m) {
|
|
||||||
JCExpression key = nameToExpression(ConstPool.NE_X_KEY);
|
|
||||||
JCExpression value = nameToExpression(ConstPool.NE_X_VAL);
|
|
||||||
JCExpression methodName = cx.treeMaker.Literal(m.fst.toString());
|
|
||||||
JCExpression thisIns = cx.treeMaker.Ident(cx.names.fromString(ConstPool.REF_THIS));
|
|
||||||
JCExpression returnClassType = m.snd.fst;
|
|
||||||
JCExpression parameterTypes = cx.treeMaker.NewArray(cx.treeMaker.Ident(cx.names.fromString(TYPE_CLASS)),
|
|
||||||
List.<JCExpression>nil(), m.snd.snd);
|
|
||||||
JCNewClass keyClass = cx.treeMaker.NewClass(null, List.<JCExpression>nil(), key,
|
|
||||||
List.of(methodName, parameterTypes), null);
|
|
||||||
JCNewClass valClass = cx.treeMaker.NewClass(null, List.<JCExpression>nil(), value,
|
|
||||||
List.of(thisIns, returnClassType), null);
|
|
||||||
JCExpression addInjectMethod = nameToExpression(ConstPool.NE_X_ADD);
|
|
||||||
JCMethodInvocation apply = cx.treeMaker.Apply(List.<JCExpression>nil(), addInjectMethod,
|
|
||||||
List.from(new JCExpression[] {keyClass, valClass}));
|
|
||||||
return cx.treeMaker.Exec(apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCStatement toGlobalNewStatement(Pair<Name, Pair<JCExpression, List<JCExpression>>> m) {
|
|
||||||
JCExpression key = nameToExpression(ConstPool.NE_W_KEY);
|
|
||||||
JCExpression value = nameToExpression(ConstPool.NE_W_VAL);
|
|
||||||
JCExpression classType = m.snd.fst;
|
|
||||||
JCExpression parameterTypes = cx.treeMaker.NewArray(cx.treeMaker.Ident(cx.names.fromString(TYPE_CLASS)),
|
|
||||||
List.<JCExpression>nil(), m.snd.snd);
|
|
||||||
JCExpression thisIns = cx.treeMaker.Ident(cx.names.fromString(ConstPool.REF_THIS));
|
|
||||||
JCExpression methodName = cx.treeMaker.Literal(m.fst.toString());
|
|
||||||
JCNewClass keyClass = cx.treeMaker.NewClass(null, List.<JCExpression>nil(), key,
|
|
||||||
List.of(classType, parameterTypes), null);
|
|
||||||
JCNewClass valClass = cx.treeMaker.NewClass(null, List.<JCExpression>nil(), value,
|
|
||||||
List.of(thisIns, methodName), null);
|
|
||||||
JCExpression addInjectMethod = nameToExpression(ConstPool.NE_W_ADD);
|
|
||||||
JCMethodInvocation apply = cx.treeMaker.Apply(List.<JCExpression>nil(), addInjectMethod,
|
|
||||||
List.from(new JCExpression[] {keyClass, valClass}));
|
|
||||||
return cx.treeMaker.Exec(apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<JCAnnotation> removeAnnotation(List<JCAnnotation> annotations, String target) {
|
|
||||||
ListBuffer<JCAnnotation> nb = new ListBuffer<>();
|
|
||||||
for (JCAnnotation i : annotations) {
|
|
||||||
if (!i.type.tsym.toString().equals(target)) {
|
|
||||||
nb.add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nb.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Name getStubbornSetterMethodName(JCAssign assign) {
|
|
||||||
String name = ((JCFieldAccess)assign.lhs).name.toString() + ConstPool.TESTABLE_SET_METHOD_PREFIX;
|
|
||||||
return cx.names.fromString(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAssignStubbornField(JCAssign expr) {
|
|
||||||
return expr.lhs.getClass().equals(JCFieldAccess.class) &&
|
|
||||||
sourceClassIns.contains(((JCIdent)((JCFieldAccess)(expr).lhs).selected).name) &&
|
|
||||||
stubbornFields.contains(((JCFieldAccess)(expr).lhs).name.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCIdent getTestableClassIdent(JCExpression clazz) {
|
|
||||||
Name className = ((JCIdent)clazz).name;
|
|
||||||
return cx.treeMaker.Ident(cx.names.fromString(className + ConstPool.TESTABLE));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -11,17 +11,16 @@ public final class ConstPool {
|
|||||||
public static final String NE_PKG = "n";
|
public static final String NE_PKG = "n";
|
||||||
public static final String NE_CLS = "e";
|
public static final String NE_CLS = "e";
|
||||||
public static final String NE_NEW = "w";
|
public static final String NE_NEW = "w";
|
||||||
public static final String NE_INK = "x";
|
public static final String NE_FUN = "f";
|
||||||
public static final String NE_PKG_CLS = "n.e";
|
public static final String NE_PKG_CLS = "n.e";
|
||||||
public static final String NE_W_KEY = "n.e.wk";
|
public static final String NE_POOL = "n.e.p";
|
||||||
public static final String NE_X_KEY = "n.e.xk";
|
public static final String NE_ADD_W = "n.e.aw";
|
||||||
public static final String NE_W_VAL = "n.e.wv";
|
public static final String NE_ADD_F = "n.e.af";
|
||||||
public static final String NE_X_VAL = "n.e.xv";
|
|
||||||
public static final String NE_W_ADD = "n.e.wa";
|
|
||||||
public static final String NE_X_ADD = "n.e.xa";
|
|
||||||
public static final String TESTABLE_GET_METHOD_PREFIX = "TestableGet";
|
public static final String TESTABLE_GET_METHOD_PREFIX = "TestableGet";
|
||||||
public static final String TESTABLE_SET_METHOD_PREFIX = "TestableSet";
|
public static final String TESTABLE_SET_METHOD_PREFIX = "TestableSet";
|
||||||
public static final String TYPE_TO_CLASS = "class";
|
public static final String TYPE_TO_CLASS = "class";
|
||||||
public static final String REF_THIS = "this";
|
public static final String REF_THIS = "this";
|
||||||
|
public static final String ANNOTATION_TESTABLE_INJECT = "com.alibaba.testable.annotation.TestableInject";
|
||||||
|
public static final String ANNOTATION_JUNIT5_SETUP = "org.junit.jupiter.api.BeforeEach";
|
||||||
|
public static final String ANNOTATION_JUNIT5_TEST = "org.junit.jupiter.api.Test";
|
||||||
}
|
}
|
||||||
|
@ -10,93 +10,35 @@ import java.util.*;
|
|||||||
|
|
||||||
public final class e {
|
public final class e {
|
||||||
|
|
||||||
/**
|
public static class p {
|
||||||
* key for contructor pool
|
public Class c; // target instance type to new / method return type
|
||||||
*/
|
public Class[] a; // constructor parameter types / member method parameter types
|
||||||
public static class wk {
|
public Object o; // object which provides substitution / object which provides substitution
|
||||||
public Class c; // target instance type to new
|
public String m; // substitutional method name / original member method name
|
||||||
public Class[] a; // constructor parameter types
|
|
||||||
|
|
||||||
public wk(Class c, Class[] a) {
|
public p(Class c, String m, Class[] a, Object o) {
|
||||||
this.c = c;
|
this.c = c;
|
||||||
this.a = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o.getClass().equals(e.wk.class) && c.equals(((e.wk)o).c) && Arrays.equals(a, ((e.wk)o).a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 31 * c.hashCode() + Arrays.hashCode(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key for method pool
|
|
||||||
*/
|
|
||||||
public static class xk {
|
|
||||||
public String m; // original member method name
|
|
||||||
public Class[] a; // member method parameter types
|
|
||||||
|
|
||||||
public xk(String m, Class[] a) {
|
|
||||||
this.m = m;
|
this.m = m;
|
||||||
this.a = a;
|
this.a = a;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o.getClass().equals(e.xk.class) && m.equals(((e.xk)o).m) && Arrays.equals(a, ((e.xk)o).a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 31 * m.hashCode() + Arrays.hashCode(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* value for contructor pool
|
|
||||||
*/
|
|
||||||
public static class wv {
|
|
||||||
public Object o; // object which provides substitution
|
|
||||||
public String m; // substitutional method name
|
|
||||||
|
|
||||||
public wv(Object o, String m) {
|
|
||||||
this.o = o;
|
this.o = o;
|
||||||
this.m = m;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static List<p> pw = new ArrayList<>();
|
||||||
* value for member method pool
|
private static List<p> pf = new ArrayList<>();
|
||||||
*/
|
|
||||||
public static class xv {
|
|
||||||
public Object o; // object which provides substitution
|
|
||||||
public Class c; // method return type
|
|
||||||
|
|
||||||
public xv(Object o, Class c) {
|
|
||||||
this.o = o;
|
|
||||||
this.c = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<wk, wv> wp = new HashMap<>();
|
|
||||||
private static Map<xk, xv> xp = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add item to contructor pool
|
* add item to contructor pool
|
||||||
*/
|
*/
|
||||||
public static void wa(wk k, wv v) {
|
public static void aw(p np) {
|
||||||
wp.put(k, v);
|
pw.add(np);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add item to method pool
|
* add item to method pool
|
||||||
*/
|
*/
|
||||||
public static void xa(xk k, xv v) {
|
public static void af(p np) {
|
||||||
xp.put(k, v);
|
pf.add(np);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,13 +49,13 @@ public final class e {
|
|||||||
for (int i = 0; i < cs.length; i++) {
|
for (int i = 0; i < cs.length; i++) {
|
||||||
cs[i] = as[i].getClass();
|
cs[i] = as[i].getClass();
|
||||||
}
|
}
|
||||||
if (!wp.isEmpty()) {
|
if (!pw.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
Pair<wk, wv> p = gwp(new wk(ct, cs));
|
p pi = gpw(ct, cs);
|
||||||
if (p != null) {
|
if (pi != null) {
|
||||||
Method m = p.snd.o.getClass().getDeclaredMethod(p.snd.m, p.fst.a);
|
Method m = pi.o.getClass().getDeclaredMethod(pi.m, pi.a);
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
return (T)m.invoke(p.snd.o, as);
|
return (T)m.invoke(pi.o, as);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
@ -133,22 +75,22 @@ public final class e {
|
|||||||
/**
|
/**
|
||||||
* subsitituion entry for member call
|
* subsitituion entry for member call
|
||||||
*/
|
*/
|
||||||
public static <T> T x(Object obj, String method, Object... as) {
|
public static <T> T f(Object obj, String mn, Object... as) {
|
||||||
Class[] cs = gcs(as);
|
Class[] cs = gcs(as);
|
||||||
if (!xp.isEmpty()) {
|
if (!pf.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
Pair<xk, xv> p = gxp(new xk(method, cs));
|
p pi = gpf(mn, cs);
|
||||||
if (p != null) {
|
if (pi != null) {
|
||||||
Method m = p.snd.o.getClass().getDeclaredMethod(p.fst.m, p.fst.a);
|
Method m = pi.o.getClass().getDeclaredMethod(pi.m, pi.a);
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
return (T)m.invoke(p.snd.o, as);
|
return (T)m.invoke(pi.o, as);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Method m = gm(obj.getClass().getDeclaredMethods(), method, cs);
|
Method m = gm(obj.getClass().getDeclaredMethods(), mn, cs);
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
m.setAccessible(true);
|
m.setAccessible(true);
|
||||||
return (T)m.invoke(obj, as);
|
return (T)m.invoke(obj, as);
|
||||||
@ -171,11 +113,11 @@ public final class e {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get method by parameter matching
|
* get method by name and parameter matching
|
||||||
*/
|
*/
|
||||||
private static Method gm(Method[] methods, String name, Class[] cs) {
|
private static Method gm(Method[] mds, String mn, Class[] cs) {
|
||||||
for (Method m : methods) {
|
for (Method m : mds) {
|
||||||
if (m.getName().equals(name) && te(m.getParameterTypes(), cs)) {
|
if (m.getName().equals(mn) && te(m.getParameterTypes(), cs)) {
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,10 +127,10 @@ public final class e {
|
|||||||
/**
|
/**
|
||||||
* get from method pool by key
|
* get from method pool by key
|
||||||
*/
|
*/
|
||||||
private static Pair<xk, xv> gxp(xk k1) {
|
private static p gpf(String mn, Class[] cs) {
|
||||||
for (xk k2 : xp.keySet()) {
|
for (p f : pf) {
|
||||||
if (k1.m.equals(k2.m) && te(k2.a, k1.a)) {
|
if (f.m.equals(mn) && te(f.a, cs)) {
|
||||||
return Pair.of(k2, xp.get(k2));
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -197,8 +139,8 @@ public final class e {
|
|||||||
/**
|
/**
|
||||||
* get contructor by parameter matching
|
* get contructor by parameter matching
|
||||||
*/
|
*/
|
||||||
private static Constructor gc(Constructor<?>[] constructors, Class[] cs) {
|
private static Constructor gc(Constructor<?>[] cons, Class[] cs) {
|
||||||
for (Constructor c : constructors) {
|
for (Constructor c : cons) {
|
||||||
if (te(c.getParameterTypes(), cs)) {
|
if (te(c.getParameterTypes(), cs)) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -209,10 +151,10 @@ public final class e {
|
|||||||
/**
|
/**
|
||||||
* get from contructor pool by key
|
* get from contructor pool by key
|
||||||
*/
|
*/
|
||||||
private static Pair<wk, wv> gwp(wk k1) {
|
private static p gpw(Class ct, Class[] cs) {
|
||||||
for (wk k2 : wp.keySet()) {
|
for (p w : pw) {
|
||||||
if (k1.c.equals(k2.c) && te(k2.a, k1.a)) {
|
if (w.c.equals(ct) && te(w.a, cs)) {
|
||||||
return Pair.of(k2, wp.get(k2));
|
return w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
Loading…
Reference in New Issue
Block a user