package ch.hsr.ifs.pasta;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
public class NodeWidget extends Composite {
private Tree tree;
public NodeWidget(final Composite parent) {
super(parent, SWT.NONE);
init();
}
private void init() {
setLayout(new FillLayout());
setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE));
tree = new Tree(this, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
tree.setLayout(new FillLayout());
tree.setHeaderVisible(true);
tree.setBounds(getParent().getClientArea());
final TreeColumn nameCol = new TreeColumn(tree, SWT.LEFT);
nameCol.setText("Name");
nameCol.setWidth(200);
final TreeColumn valueCol = new TreeColumn(tree, SWT.LEFT);
valueCol.setText("Value");
valueCol.setWidth(800);
tree.setVisible(true);
}
public void displayNode(final IASTNode node) {
try {
node.getTranslationUnit().getIndex().acquireReadLock();
clearTree();
displayName(node);
displayBindings(node);
displayImplicitNames(node);
final TreeItem typeHierarchy = createTreeItem(tree, "Type Hierarchy;");
displayTypeHierarchy(typeHierarchy, node);
final TreeItem fields = createTreeItem(tree, "Fields;@" + Integer.toString(node.hashCode(), 16));
displayFields(fields, node);
final TreeItem methods = createTreeItem(tree, "Methods;");
displayMethods(methods, node);
expandFirstLevel();
} catch (final InterruptedException e) {
e.printStackTrace();
} finally {
node.getTranslationUnit().getIndex().releaseReadLock();
}
}
private void displayTypeIfPresent(final TreeItem parent, final IBinding binding) {
try {
final Method getTypeMethod = getGetTypeMethod(binding);
if (getTypeMethod != null) {
final TreeItem parentOfType = createTreeItem(parent, "Type;");
final Object type = getTypeMethod.invoke(binding);
createTreeItem(parentOfType, "toString();" + type);
final TreeItem typeHierarchy = createTreeItem(parentOfType, "Type Hierarchy;");
displayTypeHierarchy(typeHierarchy, type);
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// PastaPlugin.log(e); // Uncomment for debugging
}
}
private Method[] getMethods(final Object node) {
if (node == null) {
return new Method[0];
}
final Class<?> clazz = node.getClass();
if (clazz == null) {
return new Method[0];
}
return clazz.getMethods();
}
private Method getGetTypeMethod(final Object o) {
final Method[] methods = getMethods(o);
for (final Method method : methods) {
if (Modifier.isPublic(method.getModifiers()) && method.getName().equals("getType")) {
return method;
}
}
return null;
}
private void displayMethods(final TreeItem parent, final Object node) {
collectMethods(parent, node.getClass());
}
private void displayName(final IASTNode node) {
createTreeItem(tree, node.getClass().getSimpleName() + ";" + safeToString(node));
}
private void clearTree() {
for (final TreeItem item : tree.getItems()) {
item.dispose();
}
}
private void displayFields(final TreeItem parent, final Object node) {
final Class<? extends Object> theClass = node.getClass();
addFieldsOfClass(theClass, parent, node);
}
private void addFieldsOfClass(final Class<? extends Object> theClass, final TreeItem parent, final Object node) {
final List<Field> fields = new ArrayList<>(Arrays.asList(theClass.getFields()));
fields.addAll(Arrays.asList(theClass.getDeclaredFields()));
for (final Field field : fields) {
makeAccessible(field);
if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) { // PS,
// don't
// want
// static
// fields
createFieldValueEntries(parent, field.getName(), getValue(field, node));
} // do not display static
}
final Class<?> superclass = theClass.getSuperclass();
if (superclass != Object.class) {
final TreeItem superfields = createTreeItem(parent, superclass.getSimpleName() + ";");
addFieldsOfClass(theClass.getSuperclass(), superfields, node);
superfields.setExpanded(true);
}
}
public void createFieldValueEntries(final TreeItem parent, final String fieldName, final Object fieldValue) {
if (fieldValue != null && fieldValue.getClass().isArray()) {
final int len = Array.getLength(fieldValue);
final TreeItem arrayitem = createTreeItem(parent, fieldName + "[" + len + "]" + ";" + fieldValue);
for (int i = 0; i < len; ++i) {
final Object entryvalue = Array.get(fieldValue, i);
final String nameForArrayField = fieldName + "[" + i + "]";
createFieldValueEntries(arrayitem, nameForArrayField, entryvalue);
}
} else {
createFieldEntrySafe(parent, fieldName, fieldValue);
}
}
public void createFieldEntrySafe(final TreeItem parent, final String nameForField, final Object fieldValue) {
// workaround for CPPASTNameBase.toString() NPE
String value = safeToString(fieldValue);
if (fieldValue != null) {
value += " : " + fieldValue.getClass().getSimpleName();
}
createTreeItem(parent, nameForField + ";" + value);
}
public String safeToString(final Object fieldValue) {
String value = "null";
if (fieldValue != null) {
try {
value = fieldValue.toString();
} catch (final Throwable t) {
PastaPlugin.log(t);
value = "tostring throws: " + t;
}
}
return value;
}
private void displayTypeHierarchy(final TreeItem parent, final Object o) {
collectSuperclasses(parent, o.getClass());
parent.setExpanded(true);
}
private void collectSuperclasses(final TreeItem superClasses, final Class<?> clazz) {
if (clazz == null) {
return;
}
final TreeItem classItem = createTreeItem(superClasses, clazz.getSimpleName() + ";");
displayInterfaceHierarchy(classItem, clazz);
collectSuperclasses(classItem, clazz.getSuperclass());
classItem.setExpanded(true);
}
private void displayInterfaceHierarchy(final TreeItem classItem, final Class<?> clazz) {
for (final Class<?> interfaceClass : clazz.getInterfaces()) {
final TreeItem interfaceItem = createTreeItem(classItem, interfaceClass.getSimpleName() + ";");
displayInterfaceHierarchy(interfaceItem, interfaceClass);
}
}
private void collectMethods(final TreeItem parentItem, final Class<?> clazz) {
if (clazz == null) {
return;
}
for (final Method method : clazz.getMethods()) {
if (Modifier.isPublic(method.getModifiers())) {
createTreeItem(parentItem, method.getName() + ";" + method.getReturnType().getSimpleName());
}
}
final Class<?> superclass = clazz.getSuperclass();
if (superclass != Object.class) {
final TreeItem supermethods = createTreeItem(parentItem, superclass.getSimpleName() + ";");
collectMethods(supermethods, superclass);
supermethods.setExpanded(true);
}
}
private void displayImplicitNames(final IASTNode node) {
if (node instanceof IASTImplicitNameOwner) {
final IASTImplicitName[] implicitNames = ((IASTImplicitNameOwner) node).getImplicitNames();
final TreeItem parent = createTreeItem(tree, "Implicit Names;" + implicitNames.length);
for (final IASTImplicitName implicitName : implicitNames) {
try {
final TreeItem implicitNameItem = createTreeItem(parent,
implicitName.resolveBinding().getName() + ";");
final TreeItem bindingsParent = createTreeItem(implicitNameItem, "Bindings;");
displayBindings(bindingsParent, node, implicitName.resolveBinding());
} catch (final Exception e) {
PastaPlugin.log(e);
}
}
}
}
private void displayBindings(final IASTNode node) {
try {
if (node instanceof IASTName) {
final IBinding binding = ((IASTName) node).resolveBinding();
final TreeItem parent = createTreeItem(tree, "Bindings;");
displayBindings(parent, node, binding);
}
} catch (final Exception e) {
PastaPlugin.log(e);
}
}
private void displayBindings(final TreeItem parent, final IASTNode node, final IBinding binding)
throws CoreException {
final IASTTranslationUnit ast = node.getTranslationUnit();
for (final IASTName decl : ast.getDeclarationsInAST(binding)) {
createTreeItem(parent, "declaration;" + decl);
}
for (final IASTName def : ast.getDefinitionsInAST(binding)) {
createTreeItem(parent, "definition;" + def);
}
for (final IASTName ref : ast.getReferences(binding)) {
createTreeItem(parent, "reference;" + ref);
}
displayTypeIfPresent(parent, binding);
final TreeItem typeHierarchy = createTreeItem(parent, "Type Hierarchy;");
displayTypeHierarchy(typeHierarchy, binding);
final TreeItem fields = createTreeItem(parent, "Fields;");
displayFields(fields, binding);
final TreeItem methods = createTreeItem(parent, "Methods;");
displayMethods(methods, binding);
}
private void makeAccessible(final Field field) {
try {
field.setAccessible(true);
final Field modifierField = Field.class.getDeclaredField("modifiers");
modifierField.setAccessible(true);
modifierField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
} catch (final Exception e) {
PastaPlugin.log(e);
}
}
private void expandFirstLevel() {
for (final TreeItem item : tree.getItems()) {
item.setExpanded(true);
}
}
private Object getValue(final Field field, final Object node) {
try {
return field.get(node);
} catch (final Throwable e) {
PastaPlugin.log(e);
return "error loading field value";
}
}
private TreeItem createTreeItem(final Tree parent, final String string) {
final TreeItem treeItem = new TreeItem(parent, SWT.NONE);
return configureTreeItem(string, treeItem);
}
private TreeItem createTreeItem(final TreeItem parent, final String content) {
final TreeItem treeItem = new TreeItem(parent, SWT.NONE);
return configureTreeItem(content, treeItem);
}
private TreeItem configureTreeItem(final String string, final TreeItem treeItem) {
treeItem.setText(string.split(";"));
treeItem.setExpanded(true);
return treeItem;
}
}