package javassist.android;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import javassist.NotFoundException;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.DuplicateMemberException;
import javassist.bytecode.ExceptionsAttribute;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
public class DalvikClassClassPath implements DalvikClassPath {
private final Class<?> clazz;
private final JarFile apk;
private DalvikClassClassPath(Class<?> c, Context ctx) {
clazz = c;
JarFile jarFile = null;
try {
final ApplicationInfo ai = ctx.getApplicationInfo();
jarFile = new JarFile(ctx, ai.sourceDir);
} catch (IOException e) {
e.printStackTrace();
}
apk = jarFile;
}
public DalvikClassClassPath(Context context) {
this(java.lang.Object.class, context);
}
@Override
public InputStream openClassfile(String classname) throws NotFoundException {
throw new NotFoundException("class file is not found.");
}
@Override
public ClassFile getClassFile(String classname) throws NotFoundException {
Class<?> clazz = null;
try {
clazz = this.apk.getClass(classname);
} catch (ClassNotFoundException e) {
throw new NotFoundException(e.getMessage(), e);
}
final Class<?> superClass = clazz.getSuperclass();
final ClassFile cf = new ClassFile(clazz.isInterface(), classname, null == superClass ? null : superClass.getName());
addFields(cf, clazz);
addConstructors(cf, clazz);
addMethods(cf, clazz);
return cf;
}
@Override
public URL find(String classname) {
if (null == apk) {
return null;
}
try {
final Class<?> cls = apk.getClass(classname);
final URL url = null == cls ? null : new URL("file", "", classname);
Log.d("%s.find(%s) = %s", getClass().getSimpleName(), classname, url);
return url;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void close() {
// nothing to do.
}
@Override
public List<FieldInfo> getClassFields(String classname, ConstPool cp) {
final Field[] fields = clazz.getDeclaredFields();
if (null == fields || 0 == fields.length) {
return null;
}
final ArrayList<FieldInfo> ret = new ArrayList<FieldInfo>();
for (Field f : fields) {
final FieldInfo fi = new FieldInfo(cp, f.getName(), Descriptor.of(f.getClass()));
ret.add(fi);
fi.setAccessFlags(f.getModifiers());
}
return ret;
}
@Override
public List<MethodInfo> getClassMethods(String classname, ConstPool cp) {
final Method[] methods = clazz.getDeclaredMethods();
final Constructor<?>[] ctors = clazz.getConstructors();
final ArrayList<MethodInfo> ret = new ArrayList<MethodInfo>();
if (null != methods && 0 != methods.length) {
for (Method m : methods) {
final MethodInfo mi = new MethodInfo(cp, m.getName(), Descriptor.ofMethod(m.getReturnType(), m.getParameterTypes()));
ret.add(mi);
buildMethodInfo(cp, mi, m);
}
}
if (null != ctors && 0 != ctors.length) {
for (Constructor<?> c : ctors) {
final MethodInfo mi = new MethodInfo(cp, "<init>", Descriptor.ofConstructor(c.getParameterTypes()));
ret.add(mi);
buildMethodInfo(cp, mi, c);
}
}
return 0 == ret.size() ? null : ret;
}
private void addFields(ClassFile cfile, Class<?> clazz) {
final Field[] fields = clazz.getDeclaredFields();
if (null != fields && 0 < fields.length) {
for (Field f : fields) {
try {
final FieldInfo fi = new FieldInfo(cfile.getConstPool(), f.getName(), Descriptor.of(f.getType()));
fi.setAccessFlags(f.getModifiers());
cfile.addField(fi);
} catch (DuplicateMemberException e) {
e.printStackTrace();
}
}
}
}
private void addConstructors(ClassFile cfile, Class<?> clazz) {
final Constructor<?>[] ctors = clazz.getDeclaredConstructors();
if (null != ctors && 0 < ctors.length) {
for (Constructor<?> c : ctors) {
final MethodInfo mi = new MethodInfo(cfile.getConstPool(), "<init>", Descriptor.ofConstructor(c.getParameterTypes()));
buildMethodInfo(cfile.getConstPool(), mi, c);
try {
cfile.addMethod(mi);
} catch (DuplicateMemberException e) {
e.printStackTrace();
}
}
}
}
private void addMethods(ClassFile cfile, Class<?> clazz) {
final Method[] methods = clazz.getDeclaredMethods();
if (null != methods && 0 < methods.length) {
for (Method m : methods) {
final MethodInfo mi = new MethodInfo(cfile.getConstPool(), m.getName(), Descriptor.ofMethod(m.getReturnType(), m.getParameterTypes()));
buildMethodInfo(cfile.getConstPool(), mi, m);
try {
cfile.addMethod(mi);
} catch (DuplicateMemberException e) {
e.printStackTrace();
}
}
}
}
private void buildMethodInfo(ConstPool cp, MethodInfo mi, Method m) {
mi.setAccessFlags(m.getModifiers());
final Class<?>[] excs = m.getExceptionTypes();
if (null != excs && 0 != excs.length) {
final ExceptionsAttribute ea = new ExceptionsAttribute(cp);
buildExceptionsAttribute(ea, excs);
mi.setExceptionsAttribute(ea);
}
}
private void buildMethodInfo(ConstPool cp, MethodInfo mi, Constructor<?> ctor) {
mi.setAccessFlags(ctor.getModifiers());
final Class<?>[] excs = ctor.getExceptionTypes();
if (null != excs && 0 != excs.length) {
final ExceptionsAttribute ea = new ExceptionsAttribute(cp);
buildExceptionsAttribute(ea, excs);
mi.setExceptionsAttribute(ea);
}
}
private void buildExceptionsAttribute(ExceptionsAttribute ea, Class<?>[] exceptions) {
if (null == exceptions || 0 == exceptions.length) {
return;
}
final String[] list = new String[exceptions.length];
for (int i = 0; i < list.length; ++i) {
list[i] = exceptions[i].getName();
}
ea.setExceptions(list);
}
}