package squill.mgen;
import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.tools.ant.BuildException;
import org.objectweb.asm.*;
public class ClassInspectionUtil {
private static final int BUFFER_SIZE = 8194;
public static class MethodInfo {
private String name;
private String desc;
private int access;
public MethodInfo(int access, String name, String desc) {
this.access = access;
this.desc = desc;
this.name = name;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public int getAccess() {
return access;
}
}
public static class ClassInfo {
public String name;
public Map<String, MethodInfo> methods = new HashMap<String, MethodInfo>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, MethodInfo> getMethods() {
return methods;
}
public void addMethod(MethodInfo methodInfo) {
methods.put(methodInfo.getName(), methodInfo);
}
}
public static class LookupClassVisitor extends ClassAdapter {
private final ClassInfo classInfo;
private Map<String, File> modelClassFiles;
public LookupClassVisitor(ClassInfo classInfo, ClassVisitor visitor, Map<String, File> modelClassFiles) {
super(visitor);
this.classInfo = classInfo;
this.modelClassFiles = modelClassFiles;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if (classInfo.getName() == null) {
classInfo.setName(name.replace('/', '.'));
}
if (superName != null && !"java/lang/Object".equals(superName)) {
inspectSuperClass(superName, classInfo, modelClassFiles);
}
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
classInfo.addMethod(new MethodInfo(access, name, desc));
return null;
}
}
private static void inspectSuperClass(String internalClassName, ClassInfo classInfo, Map<String, File> modelClassFiles) {
// Look for the superclass in the modelClasses map
File classFile = modelClassFiles.get(internalClassName.substring(internalClassName.lastIndexOf('/') + 1));
byte[] fileBytes = null;
if (classFile != null) {
fileBytes = getBytesFromFile(classFile);
} else {
// Otherwise searching on the classpath
URL classResource = ClassInspectionUtil.class.getClassLoader().getResource(internalClassName.concat(".class"));
if (classResource != null) {
fileBytes = getBytesFromURL(classResource);
} else {
throw new BuildException("Class " + internalClassName + " not found neither in the model classes fileset nor on the classpath");
}
}
ClassReader cr = new ClassReader(fileBytes);
cr.accept(new LookupClassVisitor(classInfo, new ClassWriter(0), modelClassFiles), 0);
}
public static ClassInfo inspectClass(Map<String, File> modelClassFiles, String modelTypeName) {
File classFile = modelClassFiles.get(modelTypeName);
ClassReader classReader = new ClassReader(getBytesFromFile(classFile));
ClassInfo classInfo = new ClassInfo();
classReader.accept(new LookupClassVisitor(classInfo, new ClassWriter(0), modelClassFiles), 0);
return classInfo;
}
private static byte[] getBytesFromURL(URL url) {
try {
InputStream is = url.openStream();
int count;
byte data[] = new byte[BUFFER_SIZE];
// Write the file to the file system
ByteArrayOutputStream bos = new ByteArrayOutputStream(BUFFER_SIZE);
while ((count = is.read(data, 0, BUFFER_SIZE)) != -1) {
bos.write(data, 0, count);
}
bos.flush();
bos.close();
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static byte[] getBytesFromFile(File file) {
try {
InputStream is = new FileInputStream(file);
// Get the size of the file
long length = file.length();
// Create the byte array to hold the data
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + file.getName());
}
// Close the input stream and return bytes
is.close();
return bytes;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}