package org.hotswap.agent.plugin.jvm;
import org.hotswap.agent.javassist.CtClass;
import org.hotswap.agent.javassist.CtField;
import org.hotswap.agent.javassist.CtMethod;
import org.hotswap.agent.javassist.NotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Get signature for an anonymous class to compare two versions (before and after reload).
*
* @author Jiri Bubnik
*/
public class AnonymousClassInfo {
// name of the anonymous class
String className;
// signatures
String classSignature;
String methodSignature;
String fieldsSignature;
String enclosingMethodSignature;
public AnonymousClassInfo(Class c) {
this.className = c.getName();
StringBuilder classSignature = new StringBuilder(c.getSuperclass().getName());
for (Class intef : c.getInterfaces()) {
classSignature.append(";");
classSignature.append(intef.getName());
}
this.classSignature = classSignature.toString();
StringBuilder methodsSignature = new StringBuilder();
for (Method m : c.getDeclaredMethods()) {
getMethodSignature(methodsSignature, m);
}
this.methodSignature = methodsSignature.toString();
StringBuilder fieldsSignature = new StringBuilder();
for (Field f : c.getDeclaredFields()) {
// replace declarig class for constant because of unit tests (class name change)
fieldsSignature.append(f.getType().getName());
fieldsSignature.append(" ");
fieldsSignature.append(f.getName());
fieldsSignature.append(";");
}
this.fieldsSignature = fieldsSignature.toString();
StringBuilder enclosingMethodSignature = new StringBuilder();
Method enclosingMethod = c.getEnclosingMethod();
if (enclosingMethod != null) {
getMethodSignature(enclosingMethodSignature, enclosingMethod);
}
this.enclosingMethodSignature = enclosingMethodSignature.toString();
}
public AnonymousClassInfo(String className) {
this.className = className;
}
private void getMethodSignature(StringBuilder methodsSignature, Method m) {
methodsSignature.append(m.getReturnType().getName());
methodsSignature.append(" ");
methodsSignature.append(m.getName());
methodsSignature.append("(");
for (Class paramType : m.getParameterTypes())
methodsSignature.append(paramType.getName());
methodsSignature.append(")");
methodsSignature.append(";");
}
public AnonymousClassInfo(CtClass c) {
try {
this.className = c.getName();
StringBuilder classSignature = new StringBuilder(c.getSuperclass().getName());
for (CtClass intef : c.getInterfaces()) {
classSignature.append(";");
classSignature.append(intef.getName());
}
this.classSignature = classSignature.toString();
StringBuilder methodsSignature = new StringBuilder();
for (CtMethod m : c.getDeclaredMethods()) {
getMethodSignature(methodsSignature, m);
}
this.methodSignature = methodsSignature.toString();
StringBuilder fieldsSignature = new StringBuilder();
for (CtField f : c.getDeclaredFields()) {
fieldsSignature.append(f.getType().getName());
fieldsSignature.append(" ");
fieldsSignature.append(f.getName());
fieldsSignature.append(";");
}
this.fieldsSignature = fieldsSignature.toString();
StringBuilder enclosingMethodSignature = new StringBuilder();
try {
CtMethod enclosingMethod = c.getEnclosingMethod();
if (enclosingMethod != null) {
getMethodSignature(enclosingMethodSignature, enclosingMethod);
}
} catch (Exception e) {
// OK, enclosing method not defined or out of scope
}
this.enclosingMethodSignature = enclosingMethodSignature.toString();
} catch (Throwable t) {
throw new IllegalStateException("Error creating AnonymousClassInfo from " + c.getName(), t);
}
}
private void getMethodSignature(StringBuilder methodsSignature, CtMethod m) throws NotFoundException {
methodsSignature.append(m.getReturnType().getName());
methodsSignature.append(" ");
methodsSignature.append(m.getName());
methodsSignature.append("(");
for (CtClass paramType : m.getParameterTypes())
methodsSignature.append(paramType.getName());
methodsSignature.append(")");
methodsSignature.append(";");
}
public String getClassName() {
return className;
}
public String getClassSignature() {
return classSignature;
}
public String getMethodSignature() {
return methodSignature;
}
public String getFieldsSignature() {
return fieldsSignature;
}
public String getEnclosingMethodSignature() {
return enclosingMethodSignature;
}
/**
* Exact match including enclosing method.
*/
public boolean matchExact(AnonymousClassInfo other) {
return getClassSignature().equals(other.getClassSignature()) &&
getMethodSignature().equals(other.getMethodSignature()) &&
getFieldsSignature().equals(other.getFieldsSignature()) &&
getEnclosingMethodSignature().equals(other.getEnclosingMethodSignature());
}
/**
* Exact match of class, interfaces, declared methods and fields.
* May be different enclosing method.
*/
public boolean matchSignatures(AnonymousClassInfo other) {
return getClassSignature().equals(other.getClassSignature()) &&
getMethodSignature().equals(other.getMethodSignature()) &&
getFieldsSignature().equals(other.getFieldsSignature());
}
/**
* The least matching variant - same class signature can be still resolved by hotswap.
*/
public boolean matchClassSignature(AnonymousClassInfo other) {
return getClassSignature().equals(other.getClassSignature());
}
}