package org.aspectj.apache.bcel.generic;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.Attribute;
import org.aspectj.apache.bcel.classfile.ConstantPool;
import org.aspectj.apache.bcel.classfile.Field;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.Modifiers;
import org.aspectj.apache.bcel.classfile.SourceFile;
import org.aspectj.apache.bcel.classfile.Utility;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeInvisAnnos;
import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos;
public class ClassGen extends Modifiers implements Cloneable {
private String classname;
private String superclassname;
private String filename;
private int classnameIndex = -1;
private int superclassnameIndex = -1;
private int major = Constants.MAJOR_1_1;
private int minor = Constants.MINOR_1_1;
private ConstantPool cpool;
private List<Field> fieldsList = new ArrayList<Field>();
private List<Method> methodsList = new ArrayList<Method>();
private List<Attribute> attributesList = new ArrayList<Attribute>();
private List<String> interfaceList = new ArrayList<String>();
private List<AnnotationGen> annotationsList = new ArrayList<AnnotationGen>();
public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames,
ConstantPool cpool) {
this.classname = classname;
this.superclassname = superclassname;
this.filename = filename;
this.modifiers = modifiers;
this.cpool = cpool;
if (filename != null) {
addAttribute(new SourceFile(cpool.addUtf8("SourceFile"), 2, cpool.addUtf8(filename), cpool));
}
this.classnameIndex = cpool.addClass(classname);
this.superclassnameIndex = cpool.addClass(superclassname);
if (interfacenames != null) {
for (String interfacename : interfacenames) {
addInterface(interfacename);
}
}
}
public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames) {
this(classname, superclassname, filename, modifiers, interfacenames, new ConstantPool());
}
public ClassGen(JavaClass clazz) {
classnameIndex = clazz.getClassNameIndex();
superclassnameIndex = clazz.getSuperclassNameIndex();
classname = clazz.getClassName();
superclassname = clazz.getSuperclassName();
filename = clazz.getSourceFileName();
modifiers = clazz.getModifiers();
cpool = clazz.getConstantPool().copy();
major = clazz.getMajor();
minor = clazz.getMinor();
Method[] methods = clazz.getMethods();
Field[] fields = clazz.getFields();
String[] interfaces = clazz.getInterfaceNames();
for (int i = 0; i < interfaces.length; i++) {
addInterface(interfaces[i]);
}
Attribute[] attributes = clazz.getAttributes();
for (Attribute attr : attributes) {
if (attr instanceof RuntimeVisAnnos) {
RuntimeVisAnnos rva = (RuntimeVisAnnos) attr;
List<AnnotationGen> annos = rva.getAnnotations();
for (AnnotationGen a : annos) {
annotationsList.add(new AnnotationGen(a, cpool, false));
}
} else if (attr instanceof RuntimeInvisAnnos) {
RuntimeInvisAnnos ria = (RuntimeInvisAnnos) attr;
List<AnnotationGen> annos = ria.getAnnotations();
for (AnnotationGen anno : annos) {
annotationsList.add(new AnnotationGen(anno, cpool, false));
}
} else {
attributesList.add(attr);
}
}
for (int i = 0; i < methods.length; i++) {
addMethod(methods[i]);
}
for (int i = 0; i < fields.length; i++) {
addField(fields[i]);
}
}
public JavaClass getJavaClass() {
int[] interfaces = getInterfaces();
Field[] fields = getFields();
Method[] methods = getMethods();
Collection<Attribute> attributes = null;
if (annotationsList.size() == 0) {
attributes = attributesList;
} else {
attributes = new ArrayList<Attribute>();
attributes.addAll(Utility.getAnnotationAttributes(cpool, annotationsList));
attributes.addAll(attributesList);
}
ConstantPool cp = this.cpool.getFinalConstantPool();
return new JavaClass(classnameIndex, superclassnameIndex, filename, major, minor, modifiers, cp, interfaces, fields,
methods, attributes.toArray(new Attribute[attributes.size()]));
}
public void addInterface(String name) {
interfaceList.add(name);
}
public void removeInterface(String name) {
interfaceList.remove(name);
}
public int getMajor() {
return major;
}
public void setMajor(int major) {
this.major = major;
}
public void setMinor(int minor) {
this.minor = minor;
}
public int getMinor() {
return minor;
}
public void addAttribute(Attribute a) {
attributesList.add(a);
}
public void addAnnotation(AnnotationGen a) {
annotationsList.add(a);
}
public void addMethod(Method m) {
methodsList.add(m);
}
public void addEmptyConstructor(int access_flags) {
InstructionList il = new InstructionList();
il.append(InstructionConstants.THIS);
il.append(new InvokeInstruction(Constants.INVOKESPECIAL, cpool.addMethodref(superclassname, "<init>", "()V")));
il.append(InstructionConstants.RETURN);
MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", classname, il, cpool);
mg.setMaxStack(1);
mg.setMaxLocals();
addMethod(mg.getMethod());
}
public void addField(Field f) {
fieldsList.add(f);
}
public boolean containsField(Field f) {
return fieldsList.contains(f);
}
public Field findsField(String name) {
for (Field field : fieldsList) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
public Method containsMethod(String name, String signature) {
for (Method method : methodsList) {
if (method.getName().equals(name) && method.getSignature().equals(signature)) {
return method;
}
}
return null;
}
public void removeAttribute(Attribute a) {
attributesList.remove(a);
}
public void removeAnnotation(AnnotationGen a) {
annotationsList.remove(a);
}
public void removeMethod(Method m) {
methodsList.remove(m);
}
public void replaceMethod(Method old, Method new_) {
if (new_ == null)
throw new ClassGenException("Replacement method must not be null");
int i = methodsList.indexOf(old);
if (i < 0)
methodsList.add(new_);
else
methodsList.set(i, new_);
}
public void replaceField(Field old, Field new_) {
if (new_ == null)
throw new ClassGenException("Replacement method must not be null");
int i = fieldsList.indexOf(old);
if (i < 0)
fieldsList.add(new_);
else
fieldsList.set(i, new_);
}
public void removeField(Field f) {
fieldsList.remove(f);
}
public String getClassName() {
return classname;
}
public String getSuperclassName() {
return superclassname;
}
public String getFileName() {
return filename;
}
public void setClassName(String name) {
classname = name.replace('/', '.');
classnameIndex = cpool.addClass(name);
}
public void setSuperclassName(String name) {
superclassname = name.replace('/', '.');
superclassnameIndex = cpool.addClass(name);
}
public Method[] getMethods() {
Method[] methods = new Method[methodsList.size()];
methodsList.toArray(methods);
return methods;
}
public void setMethods(Method[] methods) {
methodsList.clear();
for (int m = 0; m < methods.length; m++)
addMethod(methods[m]);
}
public void setFields(Field[] fs) {
fieldsList.clear();
for (int m = 0; m < fs.length; m++)
addField(fs[m]);
}
public void setMethodAt(Method method, int pos) {
methodsList.set(pos, method);
}
public Method getMethodAt(int pos) {
return methodsList.get(pos);
}
public String[] getInterfaceNames() {
int size = interfaceList.size();
String[] interfaces = new String[size];
interfaceList.toArray(interfaces);
return interfaces;
}
public int[] getInterfaces() {
int size = interfaceList.size();
int[] interfaces = new int[size];
for (int i = 0; i < size; i++)
interfaces[i] = cpool.addClass(interfaceList.get(i));
return interfaces;
}
public Field[] getFields() {
Field[] fields = new Field[fieldsList.size()];
fieldsList.toArray(fields);
return fields;
}
public Collection<Attribute> getAttributes() {
return attributesList;
}
public AnnotationGen[] getAnnotations() {
AnnotationGen[] annotations = new AnnotationGen[annotationsList.size()];
annotationsList.toArray(annotations);
return annotations;
}
public ConstantPool getConstantPool() {
return cpool;
}
public void setConstantPool(ConstantPool constant_pool) {
cpool = constant_pool;
}
public void setClassNameIndex(int class_name_index) {
this.classnameIndex = class_name_index;
classname = cpool.getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
}
public void setSuperclassNameIndex(int superclass_name_index) {
this.superclassnameIndex = superclass_name_index;
superclassname = cpool.getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
}
public int getSuperclassNameIndex() {
return superclassnameIndex;
}
public int getClassNameIndex() {
return classnameIndex;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
System.err.println(e);
return null;
}
}
public final boolean isAnnotation() {
return (modifiers & Constants.ACC_ANNOTATION) != 0;
}
public final boolean isEnum() {
return (modifiers & Constants.ACC_ENUM) != 0;
}
public long getSUID() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF(getClassName());
int classmods = 0;
classmods |= (isPublic() ? Constants.ACC_PUBLIC : 0);
classmods |= (isFinal() ? Constants.ACC_FINAL : 0);
classmods |= (isInterface() ? Constants.ACC_INTERFACE : 0);
if (isAbstract()) {
if (isInterface()) {
if (methodsList.size() > 0)
classmods |= Constants.ACC_ABSTRACT;
} else {
classmods |= Constants.ACC_ABSTRACT;
}
}
dos.writeInt(classmods);
String[] names = getInterfaceNames();
if (names != null) {
Arrays.sort(names);
for (int i = 0; i < names.length; i++)
dos.writeUTF(names[i]);
}
List<Field> relevantFields = new ArrayList<Field>();
for (Field field : fieldsList) {
if (!(field.isPrivate() && field.isStatic()) && !(field.isPrivate() && field.isTransient())) {
relevantFields.add(field);
}
}
Collections.sort(relevantFields, new FieldComparator());
int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
| Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
for (Field f : relevantFields) {
dos.writeUTF(f.getName());
dos.writeInt(relevantFlags & f.getModifiers());
dos.writeUTF(f.getType().getSignature());
}
List<Method> relevantMethods = new ArrayList<Method>();
List<Method> relevantCtors = new ArrayList<Method>();
boolean hasClinit = false;
for (Method m : methodsList) {
boolean couldBeInitializer = m.getName().charAt(0) == '<';
if (couldBeInitializer && m.getName().equals("<clinit>")) {
hasClinit = true;
} else if (couldBeInitializer && m.getName().equals("<init>")) {
if (!m.isPrivate())
relevantCtors.add(m);
} else {
if (!m.isPrivate())
relevantMethods.add(m);
}
}
Collections.sort(relevantCtors, new ConstructorComparator());
Collections.sort(relevantMethods, new MethodComparator());
if (hasClinit) {
dos.writeUTF("<clinit>");
dos.writeInt(Modifier.STATIC);
dos.writeUTF("()V");
}
relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
| Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | Constants.ACC_NATIVE | Constants.ACC_ABSTRACT
| Constants.ACC_STRICT;
for (Method ctor : relevantCtors) {
dos.writeUTF(ctor.getName());
dos.writeInt(relevantFlags & ctor.getModifiers());
dos.writeUTF(ctor.getSignature().replace('/', '.'));
}
for (Method m : relevantMethods) {
dos.writeUTF(m.getName());
dos.writeInt(relevantFlags & m.getModifiers());
dos.writeUTF(m.getSignature().replace('/', '.'));
}
dos.flush();
dos.close();
byte[] bs = baos.toByteArray();
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] result = md.digest(bs);
long suid = 0L;
int pos = result.length > 8 ? 7 : result.length - 1;
while (pos >= 0) {
suid = suid << 8 | ((long) result[pos--] & 0xff);
}
return suid;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Unable to calculate suid for " + getClassName() + ": " + e.toString());
}
}
private static class FieldComparator implements Comparator<Field> {
public int compare(Field f0, Field f1) {
return f0.getName().compareTo(f1.getName());
}
}
private static class ConstructorComparator implements Comparator<Method> {
public int compare(Method m0, Method m1) {
return (m0).getSignature().compareTo(m1.getSignature());
}
}
private static class MethodComparator implements Comparator<Method> {
public int compare(Method m0, Method m1) {
int result = m0.getName().compareTo(m1.getName());
if (result == 0) {
result = m0.getSignature().compareTo(m1.getSignature());
}
return result;
}
}
public boolean hasAttribute(String attributeName) {
for (Attribute attr : attributesList) {
if (attr.getName().equals(attributeName)) {
return true;
}
}
return false;
}
public Attribute getAttribute(String attributeName) {
for (Attribute attr : attributesList) {
if (attr.getName().equals(attributeName)) {
return attr;
}
}
return null;
}
}