| /******************************************************************************* |
| * 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); |
| } |
| |
| } |