blob: ea8059b4fa7117f77f7762fc8f43c5a68284a2a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.ui.examples;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
/**
*
*/
public class ASTChildConstantsGenerator extends TestCase {
private static final String LIST= "java.util.List";
public static TypeFilter fgListFilter= new NodeTypeFilter(List.class);
public static TypeFilter fgASTFilter= new NodeTypeFilter(ASTNode.class);
public static TypeFilter fgListAndASTFilter= new PrimitiveTypeFilter(true);
public static TypeFilter fgPrimitiveFilter= new PrimitiveTypeFilter(false);
public static TypeFilter fgNoFilter= new TypeFilter();
public static class TypeFilter {
public boolean accept(Class returnType) {
return true;
}
}
public static class NodeTypeFilter extends TypeFilter {
private Class fClass;
public NodeTypeFilter(Class cl) {
fClass= cl;
}
public boolean accept(Class returnType) {
return fClass.isAssignableFrom(returnType);
}
}
public static class PrimitiveTypeFilter extends TypeFilter {
private boolean fInvert;
public PrimitiveTypeFilter(boolean b) {
fInvert= b;
}
public boolean accept(Class returnType) {
return fInvert ^ (!LIST.equals(returnType.getName()) && !ASTNode.class.isAssignableFrom(returnType));
}
}
public class ClassNameComparer implements Comparator {
private Collator fCollator= Collator.getInstance();
public int compare(Object o1, Object o2) {
String name1= ((Class)o1).getName();
String name2= ((Class)o2).getName();
return fCollator.compare(name1, name2);
}
}
public class MethodNameComparer implements Comparator {
private Collator fCollator= Collator.getInstance();
public int compare(Object o1, Object o2) {
String name1= ((Method)o1).getName();
String name2= ((Method)o2).getName();
return fCollator.compare(name1, name2);
}
}
public class MethodTypeComparer implements Comparator {
private Collator fCollator= Collator.getInstance();
public int compare(Object o1, Object o2) {
Method m1= (Method)o1;
Method m2= (Method)o2;
boolean isList1= LIST.equals(m1.getReturnType().getName());
boolean isList2= LIST.equals(m2.getReturnType().getName());
if (isList1 && !isList2) {
return 1;
} else if (!isList1 && isList2) {
return -1;
}
String name1= m1.getName();
String name2= m2.getName();
return fCollator.compare(name1, name2);
}
}
private static final Class THIS= ASTChildConstantsGenerator.class;
private static HashSet fgPropertyIgnoreList;
static {
fgPropertyIgnoreList= new HashSet();
fgPropertyIgnoreList.add("setProperty");
fgPropertyIgnoreList.add("setParent");
fgPropertyIgnoreList.add("setFlags");
fgPropertyIgnoreList.add("setLeadingComment");
}
public ASTChildConstantsGenerator(String name) {
super(name);
}
public static Test allTests() {
return new TestSuite(THIS);
}
public static Test suite() {
if (false) {
return allTests();
} else {
TestSuite suite= new TestSuite();
suite.addTest(new ASTChildConstantsGenerator("createGeneratedCU"));
return suite;
}
}
protected void setUp() throws Exception {
}
protected void tearDown() throws Exception {
}
public void createGeneratedCU() throws Exception {
Class[] astClases= getASTClasses();
Arrays.sort(astClases, new ClassNameComparer());
StringBuffer buf= new StringBuffer();
buf.append("package org.eclipse.jdt.internal.corext.dom;\n\n");
buf.append("import java.util.List;");
buf.append("import org.eclipse.jdt.core.dom.*;\n\n");
buf.append("public class ASTNodeConstants {\n");
createPropertyList(astClases, buf);
createPropertyValidator(astClases, buf);
createIsListTest(astClases, buf);
createIsAttributeTest(astClases, buf);
createListAccessor(astClases, buf);
createGetNodeChild(astClases, buf);
createGetPropertyOfNode(astClases, buf);
createGetNodeChildProperties(astClases, buf);
createGetChildPropertyName(astClases, buf);
buf.append("}");
String code= buf.toString();
Hashtable options= JavaCore.getOptions();
options.put(JavaCore.FORMATTER_LINE_SPLIT, "999");
// new formatter
//TextEdit edit= ToolFactory.createCodeFormatter(options).format(CodeFormatter.K_COMPILATION_UNIT, code, 0, code.length(), 0, String.valueOf('\n'));
//Document doc= new Document(code);
//edit.apply(doc, 0);
//String res= doc.get();
// old formatter
String res= ToolFactory.createDefaultCodeFormatter(options).format(code, 0, null, String.valueOf('\n'));
System.out.print(res);
}
public void createPropertyList(Class[] astClases, StringBuffer buf) throws Exception {
HashMap properties= getPropertyList(astClases);
Object[] objects= properties.keySet().toArray();
Arrays.sort(objects, Collator.getInstance());
for (int i= 0; i < objects.length; i++) {
String curr= (String) objects[i];
buf.append("/**\n");
buf.append(" * Property for nodes of type\n");
List parents= (List) properties.get(curr);
Collections.sort(parents, new ClassNameComparer());
int lastParent= parents.size() - 1;
for (int k= 0; k <= lastParent; k++) {
Class parent= (Class) parents.get(k);
buf.append(" * <code>" + Signature.getSimpleName(parent.getName()) + "</code>");
if (k != lastParent) {
buf.append(",\n");
} else {
buf.append(".\n");
}
}
buf.append(" */\n");
buf.append("public static final int " + curr + "= " + String.valueOf(i + 1) + ";\n\n");
}
}
public void createPropertyValidator(Class[] astClases, StringBuffer buf) throws Exception {
HashMap classes= createClassList(astClases);
buf.append("\n/**");
buf.append("\n *Returns <code>true</code> if a node has the given property.");
buf.append("\n */\n");
buf.append("public static boolean hasChildProperty(ASTNode node, int property) {");
buf.append("switch (node.getNodeType()) {");
Object[] objects= classes.keySet().toArray();
Arrays.sort(objects, new ClassNameComparer());
for (int i= 0; i < objects.length; i++) {
Class curr= (Class) objects[i];
Object[] properties= ((List) classes.get(curr)).toArray();
Arrays.sort(properties, Collator.getInstance());
int nProperties= properties.length;
if (nProperties != 0) {
String name= Signature.getSimpleName(curr.getName());
buf.append("case ASTNode." + toConstantName(name) + ":");
if (nProperties == 1) {
buf.append("return property == " +properties[0] + ";");
} else if (nProperties == 2) {
buf.append("return (property == " +properties[0] + ") || (property == " +properties[1] + ");");
} else if (nProperties == 3) {
buf.append("return (property == " +properties[0] + ") || (property == " +properties[1] + ") || (property == " +properties[2] + ");");
} else {
buf.append("switch (property) {");
for (int k= 0; k < nProperties; k++) {
buf.append("case " + properties[k] + ":");
}
buf.append("return true;");
buf.append("}");
buf.append("return false;");
}
}
}
buf.append("}");
buf.append("return false;");
buf.append("}\n\n");
}
public void createIsListTest(Class[] astClases, StringBuffer buf) throws Exception {
HashSet listProperties= new HashSet();
HashSet otherProperties= new HashSet();
for (int i= 0; i < astClases.length; i++) {
Class cl= astClases[i];
Method[] properties= getProperties(cl);
for (int k= 0; k < properties.length; k++) {
Method curr= properties[k];
Class ret= curr.getReturnType();
String name= toConstantName(getPropertyName(curr));
if (!LIST.equals(ret.getName())) {
otherProperties.add(name);
if (listProperties.contains(name)) {
System.err.println("Property is list and non-list: " + name);
}
if (!fgASTFilter.accept(ret) && !fgPrimitiveFilter.accept(ret)) {
System.err.println("Property has unknown return type: " + ret.getName());
}
} else {
if (otherProperties.contains(name)) {
System.err.println("Property is list and non-list: " + name);
} else {
listProperties.add(name);
}
}
}
}
String[] lists= (String[]) listProperties.toArray(new String[listProperties.size()]);
Arrays.sort(lists, Collator.getInstance());
buf.append("\n/**");
buf.append("\n * Returns <code>true</code> if property of a node is a list property.");
buf.append("\n */\n");
buf.append("public static boolean isListProperty(int property) {");
buf.append("switch (property) {");
for (int i= 0; i < lists.length; i++) {
buf.append("case " + lists[i] + ":");
}
buf.append("return true;");
buf.append("}");
buf.append("return false;");
buf.append("}\n\n");
}
public void createIsAttributeTest(Class[] astClases, StringBuffer buf) throws Exception {
HashSet otherProperties= new HashSet();
HashSet attribProperties= new HashSet();
for (int i= 0; i < astClases.length; i++) {
Class cl= astClases[i];
Method[] properties= getProperties(cl);
for (int k= 0; k < properties.length; k++) {
Method curr= properties[k];
Class ret= curr.getReturnType();
String name= toConstantName(getPropertyName(curr));
if (fgPrimitiveFilter.accept(ret)) {
attribProperties.add(name);
if (otherProperties.contains(name)) {
System.err.println("Property is attribute and non-attribute: " + name);
}
} else {
if (attribProperties.contains(name)) {
System.err.println("Property is attribute and non-attribute: " + name);
} else {
otherProperties.add(name);
}
}
}
}
String[] lists= (String[]) attribProperties.toArray(new String[attribProperties.size()]);
Arrays.sort(lists, Collator.getInstance());
buf.append("\n/**");
buf.append("\n * Returns <code>true</code> if property of a node is an attribute property (Not a List and not an ASTNode).");
buf.append("\n */\n");
buf.append("public static boolean isAttributeProperty(int property) {");
buf.append("switch (property) {");
for (int i= 0; i < lists.length; i++) {
buf.append("case " + lists[i] + ":");
}
buf.append("return true;");
buf.append("}");
buf.append("return false;");
buf.append("}\n\n");
}
public void createListAccessor(Class[] astClases, StringBuffer buf) throws Exception {
buf.append("\n/**");
buf.append("\n * Gets a property in a list.");
buf.append("\n */\n");
buf.append("public static ASTNode getNodeChild(ASTNode node, int property, int index) {");
buf.append("if (!isListProperty(property)) {");
buf.append("throw new IllegalArgumentException();");
buf.append("}");
buf.append("return (ASTNode) ((List) getNodeChild(node, property)).get(index);");
buf.append("}\n\n");
}
public void createGetNodeChild(Class[] astClases, StringBuffer buf) throws Exception {
buf.append("\n/**");
buf.append("\n * Gets a ASTNode child by the property id. Booleans and integer attributes are returned boxed.");
buf.append("\n */\n");
buf.append("public static Object getNodeChild(ASTNode node, int property) {");
buf.append("switch (node.getNodeType()) {");
for (int i= 0; i < astClases.length; i++) {
Class curr= astClases[i];
Method[] properties= getProperties(curr, fgNoFilter);
int nProperties= properties.length;
if (nProperties != 0) {
String className= Signature.getSimpleName(curr.getName());
buf.append("case ASTNode." + toConstantName(className) + ":");
if (nProperties == 1) {
String name= getPropertyName(properties[0]);
buf.append("if (property == " + toConstantName(name) + ") {");
appendWrapped(buf, className, properties[0]);
buf.append("}");
buf.append("break;");
} else {
Arrays.sort(properties, new MethodNameComparer());
buf.append("switch (property) {");
for (int k= 0; k < nProperties; k++) {
Method meth= properties[k];
String name= getPropertyName(meth);
buf.append("case " + toConstantName(name) + ":");
appendWrapped(buf, className, meth);
}
buf.append("}");
buf.append("break;");
}
}
}
buf.append("}");
buf.append("throw new IllegalArgumentException();");
buf.append("}\n\n");
}
private void appendWrapped(StringBuffer buf, String className, Method meth) {
buf.append("return ");
String val= "((" + className + ") node)." + meth.getName() + "()";
String retType= meth.getReturnType().getName();
if ("int".equals(retType)) {
buf.append("new Integer(");
buf.append(val);
buf.append(")");
} else if ("boolean".equals(retType)){
buf.append("new Boolean(");
buf.append(val);
buf.append(")");
} else {
buf.append(val);
}
buf.append(";");
}
public void createGetPropertyOfNode(Class[] astClases, StringBuffer buf) throws Exception {
buf.append("\n/**");
buf.append("\n * Gets the child property a node is located at its parent.");
buf.append("\n */\n");
buf.append("public static int getPropertyOfNode(ASTNode node) {");
buf.append("ASTNode parent=node.getParent();");
buf.append("if (parent == null) {");
buf.append("throw new IllegalArgumentException();");
buf.append("}");
buf.append("switch (parent.getNodeType()) {");
MethodTypeComparer comparer= new MethodTypeComparer();
for (int i= 0; i < astClases.length; i++) {
Class curr= astClases[i];
Method[] properties= getProperties(curr, fgListAndASTFilter);
int nProperties= properties.length;
if (nProperties != 0) {
String className= Signature.getSimpleName(curr.getName());
buf.append("case ASTNode." + toConstantName(className) + ":");
String varName= "((" + className + ") parent)";
if (nProperties > 2) {
varName= toVariableName(className);
buf.append(className);
buf.append(" ");
buf.append(varName);
buf.append("=(");
buf.append(className);
buf.append(")parent;");
}
Arrays.sort(properties, comparer);
for (int k= 0; k < properties.length; k++) {
Method meth= properties[k];
if (k != properties.length - 1) {
buf.append("if (").append(varName).append(".");
buf.append(meth.getName());
if (LIST.equals(meth.getReturnType().getName())) {
buf.append("().contains(node))");
} else {
buf.append("() == node)");
}
}
buf.append("return ");
buf.append(toConstantName(getPropertyName(meth)));
buf.append(";");
}
}
}
buf.append("}");
buf.append("throw new IllegalArgumentException();");
buf.append("}\n\n");
}
public void createGetNodeChildProperties(Class[] astClases, StringBuffer buf) throws Exception {
buf.append("\n/**");
buf.append("\n * Returns the properties of all children.");
buf.append("\n */\n");
buf.append("public static int[] getNodeChildProperties(ASTNode node) {");
buf.append("switch (node.getNodeType()) {");
for (int i= 0; i < astClases.length; i++) {
Class curr= astClases[i];
Method[] properties= getProperties(curr, fgNoFilter);
int nProperties= properties.length;
if (nProperties != 0) {
String className= Signature.getSimpleName(curr.getName());
buf.append("case ASTNode." + toConstantName(className) + ":");
buf.append("return new int[] {");
for (int k= 0; k < properties.length; k++) {
if (k > 0) {
buf.append(",");
}
buf.append(toConstantName(getPropertyName(properties[k])));
}
buf.append("};");
}
}
buf.append("}");
buf.append("return new int[0];");
buf.append("}\n\n");
}
private void createGetChildPropertyName(Class[] astClases, StringBuffer buf) {
HashSet result= new HashSet();
for (int i= 0; i < astClases.length; i++) {
Class cl= astClases[i];
Method[] properties= getProperties(cl);
for (int k= 0; k < properties.length; k++) {
Method curr= properties[k];
String name= getPropertyName(curr);
result.add(name);
}
}
Object[] properties= result.toArray();
Arrays.sort(properties, Collator.getInstance());
buf.append("\n/**");
buf.append("\n * Returns the name of a property.");
buf.append("\n */\n");
buf.append("public static String getPropertyName(int property) {");
buf.append("switch (property) {");
for (int i= 0; i < properties.length; i++) {
String curr= toConstantName((String) properties[i]);
buf.append("case " + curr + ":");
buf.append("return ").append('"').append(curr).append('"').append("; //$NON-NLS-1$\n");
}
buf.append("}");
buf.append("throw new IllegalArgumentException();");
buf.append("}\n\n");
}
private HashMap getPropertyList(Class[] astClases) {
HashMap result= new HashMap();
for (int i= 0; i < astClases.length; i++) {
Class cl= astClases[i];
Method[] properties= getProperties(cl);
for (int k= 0; k < properties.length; k++) {
Method curr= properties[k];
String name= getPropertyName(curr);
addToListMap(result, toConstantName(name), cl);
}
}
return result;
}
private HashMap createClassList(Class[] astClases) {
HashMap result= new HashMap();
for (int i= 0; i < astClases.length; i++) {
Class cl= astClases[i];
Method[] properties= getProperties(cl);
for (int k= 0; k < properties.length; k++) {
Method curr= properties[k];
String name= getPropertyName(curr);
addToListMap(result, cl, toConstantName(name));
}
}
return result;
}
private static String getPropertyName(Method curr) {
String name= curr.getName();
if (name.startsWith("get")) {
return name.substring(3);
} else if (name.startsWith("is")) {
return name;
}
return name;
}
private void addToListMap(HashMap properties, Object key, Object value) {
ArrayList list= (ArrayList) properties.get(key);
if (list == null) {
list= new ArrayList(3);
properties.put(key, list);
}
list.add(value);
}
private static Class[] getASTClasses() {
ArrayList list= new ArrayList();
Class class1= ASTVisitor.class;
Method[] methods= class1.getMethods();
for (int i= 0; i < methods.length; i++) {
Method curr= methods[i];
if ("visit".equals(curr.getName())) {
Class param= curr.getParameterTypes()[0];
list.add(param);
}
}
return (Class[]) list.toArray(new Class[list.size()]);
}
private static Method[] getProperties(Class param) {
return getProperties(param, fgNoFilter);
}
private static Method[] getProperties(Class param, TypeFilter filter) {
ArrayList res= new ArrayList();
Method[] methods= param.getMethods();
for (int i= 0; i < methods.length; i++) {
Method curr= methods[i];
int modifiers= curr.getModifiers();
if ((modifiers & (Modifier.STATIC | Modifier.PUBLIC)) == Modifier.PUBLIC) {
String name= curr.getName();
if (!fgPropertyIgnoreList.contains(name)) {
Class returnType= curr.getReturnType();
if (returnType.isArray()) {
continue;
}
if (LIST.equals(returnType.getName())) {
if (filter.accept(returnType) ) {
res.add(curr);
}
} else if (name.startsWith("set")) {
Method getter= findGetter(methods, name.substring(3));
if (getter != null && filter.accept(getter.getReturnType())) {
res.add(getter);
}
}
}
}
}
return (Method[]) res.toArray(new Method[res.size()]);
}
private static Method findGetter(Method[] methods, String string) {
String s1= "get" + string;
String s2= "is" + string;
for (int i= 0; i < methods.length; i++) {
String name= methods[i].getName();
if (s1.equals(name) || s2.equals(name)) {
return methods[i];
}
}
return null;
}
private static String toConstantName(String string) {
StringBuffer buf= new StringBuffer();
for (int i= 0; i < string.length(); i++) {
char ch= string.charAt(i);
if (i != 0 && Character.isUpperCase(ch)) {
buf.append('_');
}
buf.append(Character.toUpperCase(ch));
}
return buf.toString();
}
private static String toVariableName(String string) {
return Character.toLowerCase(string.charAt(0)) + string.substring(1);
}
}