package net.cattaka.util.cathandsgendroid.apt;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
/**
* The hack for buggy SourceTypeBinding of eclipse.
*
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=300408">Bug
* 300408 - TypeElement.getEnclosedElements does not respect source
* order</a>
*/
class Bug300408 {
/**
* If given TypeElement is SourceTypeBinding, the order of results are corrected.
*
* @param type target
* @return the enclosed elements, or an empty list if none
*/
public static List<? extends Element> getEnclosedElementsDeclarationOrder(TypeElement type) {
List<? extends Element> result = null;
try {
Object binding = field(type, "_binding");
Class<?> sourceTypeBinding = null;
{
Class<?> c = binding.getClass();
do {
if (c.getCanonicalName().equals(
"org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding")) {
sourceTypeBinding = c;
break;
}
} while ((c = c.getSuperclass()) != null);
}
final List<Object> declarationOrder;
if (sourceTypeBinding != null) {
declarationOrder = findSourceOrder(binding);
List<Element> enclosedElements = new ArrayList<Element>(type.getEnclosedElements());
Collections.sort(enclosedElements, new Comparator<Element>() {
public int compare(Element o1, Element o2) {
try {
Object o1Binding = field(o1, "_binding");
Object o2Binding = field(o2, "_binding");
int i1 = declarationOrder.indexOf(o1Binding);
int i2 = declarationOrder.indexOf(o2Binding);
return i1 - i2;
} catch (Exception e) {
return 0;
}
}
});
result = enclosedElements;
}
} catch (Exception e) {
// ignore
}
return (result != null) ? result : type.getEnclosedElements();
}
private static List<Object> findSourceOrder(Object binding) throws Exception {
Object referenceContext = field(field(binding, "scope"), "referenceContext");
TreeMap<Integer, Object> orderedBindings = new TreeMap<Integer, Object>();
collectSourceOrder(orderedBindings, referenceContext, "methods");
collectSourceOrder(orderedBindings, referenceContext, "fields");
collectSourceOrder(orderedBindings, referenceContext, "memberTypes");
return new ArrayList<Object>(orderedBindings.values());
}
private static void collectSourceOrder(TreeMap<Integer, Object> orderedBindings,
Object referenceContext, String fieldName) throws Exception {
Object[] declarations = (Object[])field(referenceContext, fieldName);
if (declarations != null) {
for (int i = 0; i < declarations.length; i++) {
Integer declarationSourceStart = (Integer)field(declarations[i],
"declarationSourceStart");
orderedBindings.put(declarationSourceStart, field(declarations[i], "binding"));
}
}
}
private static Object field(Object o, String fieldName) throws Exception {
if (o == null) {
return null;
}
Field field = o.getClass().getField(fieldName);
field.setAccessible(true);
return field.get(o);
}
}