org.test4j.mock.faking.meta.FakeMethods Maven / Gradle / Ivy
package org.test4j.mock.faking.meta;
import g_asm.org.objectweb.asm.Type;
import org.test4j.mock.faking.util.ClassLoad;
import org.test4j.mock.faking.util.TypeUtility;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.test4j.mock.faking.meta.MethodId.getDesc;
import static org.test4j.mock.faking.meta.MethodId.getName;
public class FakeMethods {
/**
* mock行为序号
*/
public final long fakeSeqNo;
/**
* mock方法列表
*/
private final List methods = new ArrayList<>();
public FakeMethods(long fakeSeqNo) {
this.fakeSeqNo = fakeSeqNo;
}
public FakeMethod findMethod(MethodId methodId) {
FakeMethod compatible = null;
for (FakeMethod method : this.methods) {
if (!isClassMatch(methodId, method.meta)) {
continue;
}
if (Objects.equals(method.meta.methodDesc, methodId.methodDesc)) {
return method;
}
if (this.isCompatible(method.meta.methodDesc, methodId.methodDesc)) {
compatible = method;
}
}
return compatible;
}
/**
* 如果是构造函数, 必须是本类
* 如果是普通函数, 必须是本类或子类
*
* @param real
* @param fake
* @return
*/
private boolean isClassMatch(MethodId real, MethodId fake) {
if (Objects.equals(real.declaredClassDesc, fake.declaredClassDesc) || fake.declaredClassDesc == null) {
return TypeUtility.isAssignable(fake.realClass(), real.realClass());
} else {
return false;
}
}
/**
* 实际声明的方法和fake中定义的方法是否兼容
*
* @param fakeDesc
* @param declaredDesc
* @return
*/
private boolean isCompatible(String fakeDesc, String declaredDesc) {
if (!Objects.equals(getName(fakeDesc), getName(declaredDesc))) {
return false;
}
Type[] fakeTypes = Type.getArgumentTypes(getDesc(fakeDesc));
Type[] declaredTypes = Type.getArgumentTypes(getDesc(declaredDesc));
if (fakeTypes.length != declaredTypes.length) {
return false;
}
for (int index = 0; index < fakeTypes.length; index++) {
String fakeTypeName = fakeTypes[index].getClassName();
String declareTypeName = declaredTypes[index].getClassName();
if (Objects.equals(fakeTypeName, declareTypeName)) {
continue;
}
Class fakeType = ClassLoad.loadClass(fakeTypeName);
Class declaredType = ClassLoad.loadClass(declareTypeName);
if (!TypeUtility.isAssignable(declaredType, fakeType)) {
return false;
}
}
return true;
}
public void clear() {
this.methods.clear();
}
public void add(FakeMethod fakeMethod) {
this.methods.add(fakeMethod);
}
}