remove useless again

This commit is contained in:
pengc 2021-06-18 18:45:46 +08:00
parent 0ba86283ea
commit d2337bbd96
5 changed files with 39 additions and 741 deletions

View File

@ -15,6 +15,7 @@ dependencies {
implementation project(':share') implementation project(':share')
implementation 'commons-io:commons-io:2.10.0' implementation 'commons-io:commons-io:2.10.0'
implementation 'com.android.tools.build:apkzlib:4.2.1' implementation 'com.android.tools.build:apkzlib:4.2.1'
implementation 'com.beust:jcommander:1.81'
} }
sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/apksigner/java" sourceSets.main.java.srcDirs += "$rootProject.projectDir/apksigner/src/apksigner/java"

View File

@ -5,6 +5,9 @@ import static org.apache.commons.io.FileUtils.copyFile;
import com.android.tools.build.apkzlib.zip.StoredEntry; import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile; import com.android.tools.build.apkzlib.zip.ZFile;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.wind.meditor.base.BaseCommand;
import com.wind.meditor.core.FileProcesser; import com.wind.meditor.core.FileProcesser;
import com.wind.meditor.property.AttributeItem; import com.wind.meditor.property.AttributeItem;
import com.wind.meditor.property.ModificationProperty; import com.wind.meditor.property.ModificationProperty;
@ -13,7 +16,6 @@ import com.wind.meditor.utils.NodeValue;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.lsposed.lspatch.share.Constants; import org.lsposed.lspatch.share.Constants;
import org.lsposed.patch.base.BaseCommand;
import org.lsposed.patch.task.BuildAndSignApkTask; import org.lsposed.patch.task.BuildAndSignApkTask;
import org.lsposed.patch.util.ApkSignatureHelper; import org.lsposed.patch.util.ApkSignatureHelper;
import org.lsposed.patch.util.ManifestParser; import org.lsposed.patch.util.ManifestParser;
@ -32,26 +34,29 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class LSPatch extends BaseCommand { public class LSPatch {
@Parameter(description = "apk path")
private String apkPath; private String apkPath;
private String unzipApkFilePath; private String unzipApkFilePath;
@Opt(opt = "o", longOpt = "output", description = "Output .apk file, default is " + @Parameter(names = "--help", help = true, order = 0)
"$source_apk_dir/[file-name]-xposed-signed.apk", argName = "file") private boolean help = false;
@Parameter(names = {"-o", "--output"}, description = "Output apk file")
private String outputPath; private String outputPath;
@Opt(opt = "f", longOpt = "force", hasArg = false, description = "Force overwrite exists output file") @Parameter(names = {"-f", "--force"}, description = "Force overwrite exists output file")
private boolean forceOverwrite = false; private boolean forceOverwrite = false;
@Opt(opt = "p", longOpt = "proxyname", description = "Special proxy app name with full dot path", argName = "name") @Parameter(names = {"-p", "--proxyname"}, description = "Special proxy app name with full dot path")
private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub"; private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub";
@Opt(opt = "d", longOpt = "debuggable", description = "Set 1 to make the app debuggable = true, " + @Parameter(names = {"-d", "--debuggable"}, description = "Set 1 to make the app debuggable = true, set 0 to make the app debuggable = false")
"set 0 to make the app debuggable = false", argName = "0 or 1") private boolean debuggableFlag = false;
private int debuggableFlag = -1; // 0: debuggable = false 1: debuggable = true
@Opt(opt = "l", longOpt = "sigbypasslv", description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0", argName = "0-2") @Parameter(names = {"-l", "--sigbypasslv"}, description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0")
private int sigbypassLevel = 0; private int sigbypassLevel = 0;
private int dexFileCount = 0; private int dexFileCount = 0;
@ -67,59 +72,48 @@ public class LSPatch extends BaseCommand {
"lib/x86_64" "lib/x86_64"
}; };
public static void main(String... args) { private static JCommander jCommander;
new LSPatch().doMain(args);
public static void main(String... args) throws IOException {
LSPatch lsPatch = new LSPatch();
jCommander = JCommander.newBuilder()
.addObject(lsPatch)
.build();
jCommander.parse(args);
lsPatch.doCommandLine();
} }
static public void fuckIfFail(boolean b) { public void doCommandLine() throws IOException {
if (!b) { if (apkPath == null || apkPath.isEmpty()) {
throw new IllegalStateException("wtf", new Throwable("DUMPBT")); jCommander.usage();
}
}
@Override
protected void doCommandLine() throws IOException {
if (remainingArgs.length != 1) {
System.out.println();
if (remainingArgs.length == 0) {
System.out.println("Please choose one apk file you want to process. ");
}
if (remainingArgs.length > 1) {
System.out.println("This tool can only used with one apk file.");
}
usage();
return; return;
} }
apkPath = remainingArgs[0];
File srcApkFile = new File(apkPath); File srcApkFile = new File(apkPath);
if (!srcApkFile.exists()) { if (!srcApkFile.exists()) {
System.out.println("The source apk file not exsit, please choose another one. " + System.out.println("The source apk file not exsit, please choose another one.");
"current apk file is = " + apkPath);
return; return;
} }
File finalApk = new File(String.format("%s-%s-unsigned.apk", getBaseName(srcApkFile.getAbsolutePath()), File finalApk = new File(String.format("%s-%s-unsigned.apk", srcApkFile.getName(),
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel)); Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel));
FileUtils.copyFile(srcApkFile, finalApk); FileUtils.copyFile(srcApkFile, finalApk);
ZFile zFile = new ZFile(finalApk); ZFile zFile = ZFile.openReadWrite(finalApk);
String currentDir = new File(".").getAbsolutePath(); String currentDir = new File(".").getAbsolutePath();
System.out.println("currentDir: " + currentDir); System.out.println("work dir: " + currentDir);
System.out.println("apkPath: " + apkPath); System.out.println("apk path: " + apkPath);
if (outputPath == null || outputPath.length() == 0) { if (outputPath == null || outputPath.length() == 0) {
String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel; String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel;
outputPath = String.format("%s-%s-xposed-signed.apk", getBaseName(apkPath), sig); outputPath = String.format("%s-%s-xposed-signed.apk", new File(apkPath).getName(), sig);
} }
File outputFile = new File(outputPath); File outputFile = new File(outputPath);
if (outputFile.exists() && !forceOverwrite) { if (outputFile.exists() && !forceOverwrite) {
System.err.println(outputPath + " exists, use --force to overwrite"); System.err.println(outputPath + " exists, use --force to overwrite");
usage();
return; return;
} }
@ -130,7 +124,7 @@ public class LSPatch extends BaseCommand {
outputApkFileParentPath = absPath.substring(0, index); outputApkFileParentPath = absPath.substring(0, index);
} }
String apkFileName = getBaseName(srcApkFile); String apkFileName = srcApkFile.getName();
String tempFilePath = outputApkFileParentPath + File.separator + String tempFilePath = outputApkFileParentPath + File.separator +
currentTimeStr() + "-tmp" + File.separator; currentTimeStr() + "-tmp" + File.separator;
@ -252,10 +246,7 @@ public class LSPatch extends BaseCommand {
private void modifyManifestFile(String filePath, String dstFilePath) { private void modifyManifestFile(String filePath, String dstFilePath) {
ModificationProperty property = new ModificationProperty(); ModificationProperty property = new ModificationProperty();
if (debuggableFlag >= 0) { property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag));
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag != 0));
}
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true)); property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName)); property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName));

View File

@ -1,429 +0,0 @@
package org.lsposed.patch.base;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Created by Wind
*/
public abstract class BaseCommand {
private String onlineHelp;
protected Map<String, Option> optMap = new HashMap<String, Option>();
@Opt(opt = "h", longOpt = "help", hasArg = false, description = "Print this help message")
private boolean printHelp = false;
protected String[] remainingArgs;
protected String[] orginalArgs;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD })
static public @interface Opt {
String argName() default "";
String description() default "";
boolean hasArg() default true;
String longOpt() default "";
String opt() default "";
boolean required() default false;
}
static protected class Option implements Comparable<Option> {
public String argName = "arg";
public String description;
public Field field;
public boolean hasArg = true;
public String longOpt;
public String opt;
public boolean required = false;
@Override
public int compareTo(Option o) {
int result = s(this.opt, o.opt);
if (result == 0) {
result = s(this.longOpt, o.longOpt);
if (result == 0) {
result = s(this.argName, o.argName);
if (result == 0) {
result = s(this.description, o.description);
}
}
}
return result;
}
private static int s(String a, String b) {
if (a != null && b != null) {
return a.compareTo(b);
} else if (a != null) {
return 1;
} else if (b != null) {
return -1;
} else {
return 0;
}
}
public String getOptAndLongOpt() {
StringBuilder sb = new StringBuilder();
boolean havePrev = false;
if (opt != null && opt.length() > 0) {
sb.append("-").append(opt);
havePrev = true;
}
if (longOpt != null && longOpt.length() > 0) {
if (havePrev) {
sb.append(", ");
}
sb.append("--").append(longOpt);
}
return sb.toString();
}
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE })
static public @interface Syntax {
String cmd();
String desc() default "";
String onlineHelp() default "";
String syntax() default "";
}
public void doMain(String... args) {
try {
initOptions();
parseSetArgs(args);
doCommandLine();
} catch (HelpException e) {
String msg = e.getMessage();
if (msg != null && msg.length() > 0) {
System.err.println("ERROR: " + msg);
}
usage();
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
protected abstract void doCommandLine() throws Exception;
protected String getVersionString() {
return getClass().getPackage().getImplementationVersion();
}
protected void initOptions() {
initOptionFromClass(this.getClass());
}
protected void initOptionFromClass(Class<?> clz) {
if (clz == null) {
return;
} else {
initOptionFromClass(clz.getSuperclass());
}
Syntax syntax = clz.getAnnotation(Syntax.class);
if (syntax != null) {
this.onlineHelp = syntax.onlineHelp();
}
Field[] fs = clz.getDeclaredFields();
for (Field f : fs) {
Opt opt = f.getAnnotation(Opt.class);
if (opt != null) {
f.setAccessible(true);
Option option = new Option();
option.field = f;
option.description = opt.description();
option.hasArg = opt.hasArg();
option.required = opt.required();
if ("".equals(opt.longOpt()) && "".equals(opt.opt())) { // into automode
option.longOpt = fromCamel(f.getName());
if (f.getType().equals(boolean.class)) {
option.hasArg=false;
try {
if (f.getBoolean(this)) {
throw new RuntimeException("the value of " + f +
" must be false, as it is declared as no args");
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
checkConflict(option, "--" + option.longOpt);
continue;
}
if (!opt.hasArg()) {
if (!f.getType().equals(boolean.class)) {
throw new RuntimeException("the type of " + f
+ " must be boolean, as it is declared as no args");
}
try {
if (f.getBoolean(this)) {
throw new RuntimeException("the value of " + f +
" must be false, as it is declared as no args");
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
boolean haveLongOpt = false;
if (!"".equals(opt.longOpt())) {
option.longOpt = opt.longOpt();
checkConflict(option, "--" + option.longOpt);
haveLongOpt = true;
}
if (!"".equals(opt.argName())) {
option.argName = opt.argName();
}
if (!"".equals(opt.opt())) {
option.opt = opt.opt();
checkConflict(option, "-" + option.opt);
} else {
if (!haveLongOpt) {
throw new RuntimeException("opt or longOpt is not set in @Opt(...) " + f);
}
}
}
}
}
private void checkConflict(Option option, String key) {
if (optMap.containsKey(key)) {
Option preOption = optMap.get(key);
throw new RuntimeException(String.format("[@Opt(...) %s] conflict with [@Opt(...) %s]",
preOption.field.toString(), option.field
));
}
optMap.put(key, option);
}
private static String fromCamel(String name) {
if (name.length() == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
char[] charArray = name.toCharArray();
sb.append(Character.toLowerCase(charArray[0]));
for (int i = 1; i < charArray.length; i++) {
char c = charArray[i];
if (Character.isUpperCase(c)) {
sb.append("-").append(Character.toLowerCase(c));
} else {
sb.append(c);
}
}
return sb.toString();
}
protected void parseSetArgs(String... args) throws IllegalArgumentException, IllegalAccessException {
this.orginalArgs = args;
List<String> remainsOptions = new ArrayList<>();
Set<Option> requiredOpts = collectRequriedOptions(optMap);
Option needArgOpt = null;
for (String s : args) {
if (needArgOpt != null) {
Field field = needArgOpt.field;
Class clazz = field.getType();
if (clazz.equals(List.class)) {
try {
List<Object> object = ((List<Object>) field.get(this));
// 获取List对象的泛型类型
ParameterizedType listGenericType = (ParameterizedType) field.getGenericType();
Type[] listActualTypeArguments = listGenericType.getActualTypeArguments();
Class typeClazz = (Class) listActualTypeArguments[0];
object.add(convert(s, typeClazz));
} catch (Exception e) {
e.printStackTrace();
}
} else {
field.set(this, convert(s, clazz));
}
needArgOpt = null;
} else if (s.startsWith("-")) {// its a short or long option
Option opt = optMap.get(s);
requiredOpts.remove(opt);
if (opt == null) {
System.err.println("ERROR: Unrecognized option: " + s);
throw new HelpException();
} else {
if (opt.hasArg) {
needArgOpt = opt;
} else {
opt.field.set(this, true);
}
}
} else {
remainsOptions.add(s);
}
}
if (needArgOpt != null) {
System.err.println("ERROR: Option " + needArgOpt.getOptAndLongOpt() + " need an argument value");
throw new HelpException();
}
this.remainingArgs = remainsOptions.toArray(new String[remainsOptions.size()]);
if (this.printHelp) {
throw new HelpException();
}
if (!requiredOpts.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append("ERROR: Options: ");
boolean first = true;
for (Option option : requiredOpts) {
if (first) {
first = false;
} else {
sb.append(" and ");
}
sb.append(option.getOptAndLongOpt());
}
sb.append(" is required");
System.err.println(sb.toString());
throw new HelpException();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected Object convert(String value, Class type) {
if (type.equals(String.class)) {
return value;
}
if (type.equals(int.class) || type.equals(Integer.class)) {
return Integer.parseInt(value);
}
if (type.equals(long.class) || type.equals(Long.class)) {
return Long.parseLong(value);
}
if (type.equals(float.class) || type.equals(Float.class)) {
return Float.parseFloat(value);
}
if (type.equals(double.class) || type.equals(Double.class)) {
return Double.parseDouble(value);
}
if (type.equals(boolean.class) || type.equals(Boolean.class)) {
return Boolean.parseBoolean(value);
}
if (type.equals(File.class)) {
return new File(value);
}
if (type.equals(Path.class)) {
return new File(value).toPath();
}
try {
type.asSubclass(Enum.class);
return Enum.valueOf(type, value);
} catch (Exception e) {
}
throw new RuntimeException("can't convert [" + value + "] to type " + type);
}
private Set<Option> collectRequriedOptions(Map<String, Option> optMap) {
Set<Option> options = new HashSet<Option>();
for (Map.Entry<String, Option> e : optMap.entrySet()) {
Option option = e.getValue();
if (option.required) {
options.add(option);
}
}
return options;
}
@SuppressWarnings("serial")
protected static class HelpException extends RuntimeException {
public HelpException() {
super();
}
public HelpException(String message) {
super(message);
}
}
protected void usage() {
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.err, StandardCharsets.UTF_8), true);
out.println();
if (this.optMap.size() > 0) {
out.println("Options:");
out.println();
}
TreeSet<Option> options = new TreeSet<>(this.optMap.values());
StringBuilder sb = new StringBuilder();
for (Option option : options) {
sb.setLength(0);
sb.append(" ").append(option.getOptAndLongOpt());
if (option.hasArg) {
sb.append(" <").append(option.argName).append(">");
}
String desc = option.description;
if (desc == null || desc.length() == 0) {// no description
out.println(sb);
} else {
int maxWidth = 15;
if(sb.length() >= maxWidth) {
sb.append('\n');
sb.append(" ".repeat(maxWidth));
}
else {
sb.append(" ".repeat(maxWidth - sb.length()));
}
sb.append(desc);
out.println(sb);
}
out.println();
}
String ver = getVersionString();
if (ver != null && !"".equals(ver)) {
out.println("version: " + ver);
}
out.flush();
}
public static String getBaseName(String fn) {
int x = fn.lastIndexOf('.');
return x >= 0 ? fn.substring(0, x) : fn;
}
// 获取文件不包含后缀的名称
public static String getBaseName(File fn) {
return getBaseName(fn.getName());
}
}

View File

@ -50,7 +50,9 @@ public class BuildAndSignApkTask implements Runnable {
// delete unsigned apk file // delete unsigned apk file
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) { if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
LSPatch.fuckIfFail(unsignedApkFile.delete()); if (!unsignedApkFile.delete()) {
throw new IllegalStateException("wtf");
}
} }
} }
catch (Exception err) { catch (Exception err) {

View File

@ -1,267 +0,0 @@
package org.lsposed.patch.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectUtils {
//获取类的实例的变量的值
public static Object getField(Object receiver, String fieldName) {
return getField(null, receiver, fieldName);
}
//获取类的静态变量的值
public static Object getField(String className, String fieldName) {
return getField(className, null, fieldName);
}
public static Object getField(Class<?> clazz, String className, String fieldName, Object receiver) {
try {
if (clazz == null) {
clazz = Class.forName(className);
}
Field field = clazz.getDeclaredField(fieldName);
if (field == null) {
return null;
}
field.setAccessible(true);
return field.get(receiver);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
private static Object getField(String className, Object receiver, String fieldName) {
Class<?> clazz = null;
Field field;
if (className != null && className.length() > 0) {
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
if (receiver != null) {
clazz = receiver.getClass();
}
}
if (clazz == null) {
return null;
}
try {
field = findField(clazz, fieldName);
if (field == null) {
return null;
}
field.setAccessible(true);
return field.get(receiver);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return null;
}
public static Object setField(Object receiver, String fieldName, Object value) {
try {
Field field;
field = findField(receiver.getClass(), fieldName);
if (field == null) {
return null;
}
field.setAccessible(true);
Object old = field.get(receiver);
field.set(receiver, value);
return old;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return null;
}
public static Object setField(Class<?> clazz, Object receiver, String fieldName, Object value) {
try {
Field field;
field = findField(clazz, fieldName);
if (field == null) {
return null;
}
field.setAccessible(true);
Object old = field.get(receiver);
field.set(receiver, value);
return old;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return null;
}
public static Object callMethod(Object receiver, String methodName, Object... params) {
return callMethod(null, receiver, methodName, params);
}
public static Object setField(String clazzName, Object receiver, String fieldName, Object value) {
try {
Class<?> clazz = Class.forName(clazzName);
Field field;
field = findField(clazz, fieldName);
if (field == null) {
return null;
}
field.setAccessible(true);
Object old = field.get(receiver);
field.set(receiver, value);
return old;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static Object callMethod(String className, String methodName, Object... params) {
return callMethod(className, null, methodName, params);
}
public static Object callMethod(Class<?> clazz, String className, String methodName, Object receiver,
Class[] types, Object... params) {
try {
if (clazz == null) {
clazz = Class.forName(className);
}
Method method = clazz.getDeclaredMethod(methodName, types);
method.setAccessible(true);
return method.invoke(receiver, params);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
private static Object callMethod(String className, Object receiver, String methodName, Object... params) {
Class<?> clazz = null;
if (className != null && className.length() > 0) {
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
if (receiver != null) {
clazz = receiver.getClass();
}
}
if (clazz == null) {
return null;
}
try {
Method method = findMethod(clazz, methodName, params);
if (method == null) {
return null;
}
method.setAccessible(true);
return method.invoke(receiver, params);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static Method findMethod(Class<?> clazz, String name, Object... arg) {
Method[] methods = clazz.getMethods();
Method method = null;
for (Method m : methods) {
if (methodFitParam(m, name, arg)) {
method = m;
break;
}
}
if (method == null) {
method = findDeclaredMethod(clazz, name, arg);
}
return method;
}
private static Method findDeclaredMethod(Class<?> clazz, String name, Object... arg) {
Method[] methods = clazz.getDeclaredMethods();
Method method = null;
for (Method m : methods) {
if (methodFitParam(m, name, arg)) {
method = m;
break;
}
}
if (method == null) {
if (clazz.equals(Object.class)) {
return null;
}
return findDeclaredMethod(clazz.getSuperclass(), name, arg);
}
return method;
}
private static boolean methodFitParam(Method method, String methodName, Object... arg) {
if (!methodName.equals(method.getName())) {
return false;
}
Class<?>[] paramTypes = method.getParameterTypes();
if (arg == null || arg.length == 0) {
return paramTypes == null || paramTypes.length == 0;
}
if (paramTypes.length != arg.length) {
return false;
}
for (int i = 0; i < arg.length; ++i) {
Object ar = arg[i];
Class<?> paramT = paramTypes[i];
if (ar == null) {
continue;
}
//TODO for primitive type
if (paramT.isPrimitive()) {
continue;
}
if (!paramT.isInstance(ar)) {
return false;
}
}
return true;
}
private static Field findField(Class<?> clazz, String name) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
if (clazz.equals(Object.class)) {
e.printStackTrace();
return null;
}
Class<?> base = clazz.getSuperclass();
return findField(base, name);
}
}
}