package org.hotswap.agent.util.signature;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
/**
* ClassSignatureBase. Base class for class signature evaluation
*
* @author Erki Ehtla, Vladimir Dvorak
*/
public abstract class ClassSignatureBase {
private static final String[] IGNORED_METHODS = new String[] { "annotationType", "equals", "hashCode", "toString" };
private final Set<ClassSignatureElement> elements = new HashSet<>();
/**
* Evaluate and return signature value
*
* @return the signature value
* @throws Exception
*/
public abstract String getValue() throws Exception;
/**
* Adds the signature elements to set of used signature elements
*
* @param elems
*/
public void addSignatureElements(ClassSignatureElement elems[]) {
for (ClassSignatureElement element : elems) {
elements.add(element);
}
}
/**
* Check if given signature element is set.
*
* @param element
* @return true, if has given element
*/
public boolean hasElement(ClassSignatureElement element) {
return elements.contains(element);
}
protected String annotationToString(Object[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
a = sort(a);
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0;i < a.length; i++) {
Annotation object = (Annotation) a[i];
Method[] declaredMethods = object.getClass().getDeclaredMethods();
b.append("(");
boolean printComma = false;
for (Method method : declaredMethods) {
if (Arrays.binarySearch(IGNORED_METHODS, method.getName()) < 0) {
Object value = getAnnotationValue(object, method.getName());
if (value != null) {
if (printComma) {
b.append(",");
} else {
printComma = true;
}
b.append(method.getName() + "=" + value.getClass() + ":" + value);
}
}
}
b.append(")");
// TODO : sometimes for CtFile object.annotationType() is not known an it fails here
// v.d. : uncommented in v1.1 alpha with javassist update (3.21) to check if there is still problem
b.append(object.annotationType().getName());
if (i<a.length-1) {
b.append(",");
}
}
b.append(']');
return b.toString();
}
protected String annotationToString(Object[][] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
a = sort(a);
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0;; i++) {
Object[] object = a[i];
b.append(annotationToString(object));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
private <T> T[] sort(T[] a) {
a = Arrays.copyOf(a, a.length);
Arrays.sort(a, ToStringComparator.INSTANCE);
return a;
}
private <T> T[][] sort(T[][] a) {
a = Arrays.copyOf(a, a.length);
Arrays.sort(a, ToStringComparator.INSTANCE);
for (Object[] objects : a) {
Arrays.sort(objects, ToStringComparator.INSTANCE);
}
return a;
}
private Object getAnnotationValue(Annotation annotation, String attributeName) {
Method method = null;
boolean acessibleSet = false;
try {
method = annotation.annotationType().getDeclaredMethod(attributeName);
acessibleSet = makeAccessible(method);
return method.invoke(annotation);
} catch (Exception ex) {
return null;
} finally {
if (method != null && acessibleSet) {
method.setAccessible(false);
}
}
}
private boolean makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
return true;
}
return false;
}
protected static class ToStringComparator implements Comparator<Object> {
public static final ToStringComparator INSTANCE = new ToStringComparator();
@Override
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
}
}