diff options
21 files changed, 1565 insertions, 714 deletions
diff --git a/contrib/javac-source.zip b/contrib/javac-source.zip Binary files differnew file mode 100644 index 00000000..7d989ac6 --- /dev/null +++ b/contrib/javac-source.zip diff --git a/src/lombok/core/AST.java b/src/lombok/core/AST.java new file mode 100644 index 00000000..99902672 --- /dev/null +++ b/src/lombok/core/AST.java @@ -0,0 +1,268 @@ +package lombok.core; + +import static lombok.Lombok.sneakyThrow; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +public abstract class AST<N> { + public enum Kind { + COMPILATION_UNIT, TYPE, FIELD, INITIALIZER, METHOD, ANNOTATION, ARGUMENT, LOCAL, STATEMENT; + } + + private Node top; + private final String fileName; + private Map<N, Void> identityDetector = new IdentityHashMap<N, Void>(); + private Map<N, Node> nodeMap = new IdentityHashMap<N, Node>(); + + protected AST(String fileName) { + this.fileName = fileName == null ? "(unknown).java" : fileName; + } + + protected void setTop(Node top) { + this.top = top; + } + + public abstract String getPackageDeclaration(); + + public abstract Collection<String> getImportStatements(); + + protected <T extends Node> T putInMap(T parent) { + nodeMap.put(parent.get(), parent); + identityDetector.put(parent.get(), null); + return parent; + } + + protected Map<N, Node> getNodeMap() { + return nodeMap; + } + + protected void clearState() { + identityDetector = new IdentityHashMap<N, Void>(); + nodeMap = new IdentityHashMap<N, Node>(); + } + + protected boolean alreadyHandled(N node) { + return identityDetector.containsKey(node); + } + + protected void setAsHandled(N node) { + identityDetector.put(node, null); + } + + public String getFileName() { + return fileName; + } + + public Node top() { + return top; + } + + public Node get(N node) { + return nodeMap.get(node); + } + + public abstract class Node { + protected final Kind kind; + protected final N node; + protected final Collection<? extends Node> children; + protected Node parent; + protected boolean handled; + protected boolean isStructurallySignificant; + + protected Node(N node, Collection<? extends Node> children, Kind kind) { + this.kind = kind; + this.node = node; + this.children = children == null ? Collections.<Node>emptyList() : children; + for ( Node child : this.children ) child.parent = this; + this.isStructurallySignificant = calculateIsStructurallySignificant(); + } + + public String getPackageDeclaration() { + return AST.this.getPackageDeclaration(); + } + + public Collection<String> getImportStatements() { + return AST.this.getImportStatements(); + } + + protected abstract boolean calculateIsStructurallySignificant(); + + public N get() { + return node; + } + + public Kind getKind() { + return kind; + } + + /** + * Return the name of your type (simple name), method, field, or local variable. Return null if this + * node doesn't really have a name, such as initializers, while statements, etc. + */ + public abstract String getName(); + + /** Returns the structurally significant node that encloses this one. + * + * @see #isStructurallySignificant() + */ + public Node up() { + Node result = (Node)parent; + while ( result != null && !result.isStructurallySignificant ) result = (Node)result.parent; + return result; + } + + /** + * Returns the direct parent node in the AST tree of this node. For example, a local variable declaration's + * direct parent can be e.g. an If block, but its up() Node is the Method that contains it. + */ + public Node directUp() { + return parent; + } + + public Collection<? extends Node> down() { + return children; + } + + public boolean isHandled() { + return handled; + } + + public Node setHandled() { + this.handled = true; + return this; + } + + public Node top() { + return top; + } + + public String getFileName() { + return fileName; + } + + public abstract void addError(String message); + + public abstract void addWarning(String message); + + /** + * Structurally significant means: LocalDeclaration, TypeDeclaration, MethodDeclaration, ConstructorDeclaration, + * FieldDeclaration, Initializer, and CompilationUnitDeclaration. + * The rest is e.g. if statements, while loops, etc. + */ + public boolean isStructurallySignificant() { + return isStructurallySignificant; + } + } + + protected static class FieldAccess { + public final Field field; + public final int dim; + + FieldAccess(Field field, int dim) { + this.field = field; + this.dim = dim; + } + } + + private static Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>(); + protected Collection<FieldAccess> fieldsOf(Class<?> c) { + Collection<FieldAccess> fields = fieldsOfASTClasses.get(c); + if ( fields != null ) return fields; + + fields = new ArrayList<FieldAccess>(); + getFields(c, fields); + fieldsOfASTClasses.put(c, fields); + return fields; + } + + private void getFields(Class<?> c, Collection<FieldAccess> fields) { + if ( c == Object.class || c == null ) return; + for ( Field f : c.getDeclaredFields() ) { + if ( Modifier.isStatic(f.getModifiers()) ) continue; + Class<?> t = f.getType(); + int dim = 0; + + if ( t.isArray() ) { + while ( t.isArray() ) { + dim++; + t = t.getComponentType(); + } + } else if ( Collection.class.isAssignableFrom(t) ) { + while ( Collection.class.isAssignableFrom(t) ) { + dim++; + t = getComponentType(f.getGenericType()); + } + } + + for ( Class<?> statementType : getStatementTypes() ) { + if ( statementType.isAssignableFrom(t) ) { + f.setAccessible(true); + fields.add(new FieldAccess(f, dim)); + break; + } + } + } + getFields(c.getSuperclass(), fields); + } + + private Class<?> getComponentType(Type type) { + if ( type instanceof ParameterizedType ) { + Type component = ((ParameterizedType)type).getActualTypeArguments()[0]; + return component instanceof Class<?> ? (Class<?>)component : Object.class; + } else return Object.class; + } + + protected abstract Collection<Class<? extends N>> getStatementTypes(); + + protected <T extends Node> Collection<T> buildWithField(Class<T> nodeType, N statement, FieldAccess fa) { + List<T> list = new ArrayList<T>(); + buildWithField0(nodeType, statement, fa, list); + return list; + } + + private <T extends Node> void buildWithField0(Class<T> nodeType, N child, FieldAccess fa, Collection<T> list) { + try { + Object o = fa.field.get(child); + if ( o == null ) return; + if ( fa.dim == 0 ) { + Node node = buildStatement(o); + if ( node != null ) list.add(nodeType.cast(node)); + } else if ( o.getClass().isArray() ) buildWithArray(nodeType, o, list, fa.dim); + else if ( Collection.class.isInstance(o) ) buildWithCollection(nodeType, o, list, fa.dim); + } catch ( IllegalAccessException e ) { + sneakyThrow(e); + } + } + + private <T extends Node> void buildWithArray(Class<T> nodeType, Object array, Collection<T> list, int dim) { + if ( dim == 1 ) for ( Object v : (Object[])array ) { + if ( v == null ) continue; + Node node = buildStatement(v); + if ( node != null ) list.add(nodeType.cast(node)); + } else for ( Object v : (Object[])array ) { + buildWithArray(nodeType, v, list, dim-1); + } + } + + private <T extends Node> void buildWithCollection(Class<T> nodeType, Object collection, Collection<T> list, int dim) { + if ( dim == 1 ) for ( Object v : (Collection<?>)collection ) { + if ( v == null ) continue; + Node node = buildStatement(v); + if ( node != null ) list.add(nodeType.cast(node)); + } else for ( Object v : (Collection<?>)collection ) { + buildWithCollection(nodeType, v, list, dim-1); + } + } + + protected abstract Node buildStatement(Object statement); +} diff --git a/src/lombok/core/AnnotationValues.java b/src/lombok/core/AnnotationValues.java new file mode 100644 index 00000000..ec17e34f --- /dev/null +++ b/src/lombok/core/AnnotationValues.java @@ -0,0 +1,281 @@ +package lombok.core; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class AnnotationValues<A extends Annotation> { + private final Class<A> type; + private final Map<String, AnnotationValue> values; + private final AST<?>.Node ast; + + public static class AnnotationValue { + public final List<String> raws; + public final List<Object> valueGuesses; + private final AST<?>.Node node; + + /** + * 'raw' should be the exact expression, for example '5+7', 'AccessLevel.PUBLIC', or 'int.class'. + * 'valueGuess' should be a likely guess at the real value intended. + * + * For classes, supply the class name (qualified or not) as a string.<br /> + * For enums, supply the simple name part (everything after the last dot) as a string.<br /> + */ + public AnnotationValue(AST<?>.Node node, String raw, Object valueGuess) { + this.node = node; + this.raws = Collections.singletonList(raw); + this.valueGuesses = Collections.singletonList(valueGuess); + } + + /** When the value is an array type. */ + public AnnotationValue(AST<?>.Node node, List<String> raws, List<Object> valueGuesses) { + this.node = node; + this.raws = raws; + this.valueGuesses = valueGuesses; + } + + /** + * Override this if you want more specific behaviour (e.g. get the source position just right). + * + * @param message English message with the problem. + * @param valueIdx The index into the values for this annotation key that caused the problem. + * -1 for a problem that applies to all values, otherwise the 0-based index into an array of values. + * If there is no array for this value (e.g. value=1 instead of value={1,2}), then always -1 or 0. + */ + public void setError(String message, int valueIdx) { + node.addError(message); + } + } + + public AnnotationValues(Class<A> type, Map<String, AnnotationValue> values, AST<?>.Node ast) { + this.type = type; + this.values = values; + this.ast = ast; + } + + public static class AnnotationValueDecodeFail extends RuntimeException { + private static final long serialVersionUID = 1L; + + public final int idx; + public final AnnotationValue owner; + + public AnnotationValueDecodeFail(AnnotationValue owner, String msg, int idx) { + super(msg); + this.idx = idx; + this.owner = owner; + } + } + + private static AnnotationValueDecodeFail makeNoDefaultFail(AnnotationValue owner, Method method) { + return new AnnotationValueDecodeFail(owner, + "No value supplied but " + method.getName() + " has no default either.", -1); + } + + @SuppressWarnings("unchecked") + public A getInstance() throws AnnotationValueDecodeFail { + InvocationHandler invocations = new InvocationHandler() { + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + AnnotationValue v = values.get(method.getName()); + if ( v == null ) { + Object defaultValue = method.getDefaultValue(); + if ( defaultValue != null ) return defaultValue; + throw makeNoDefaultFail(v, method); + } + + boolean isArray = false; + Class<?> expected = method.getReturnType(); + Object array = null; + if ( expected.isArray() ) { + isArray = true; + expected = expected.getComponentType(); + array = Array.newInstance(expected, 1); + } + + if ( !isArray && v.valueGuesses.size() > 1 ) { + System.out.println(v.valueGuesses.size() + ": " + v.valueGuesses); + throw new AnnotationValueDecodeFail(v, + "Expected a single value, but " + method.getName() + " has an array of values", -1); + } + + if ( v.valueGuesses.size() == 0 && !isArray ) { + Object defaultValue = method.getDefaultValue(); + if ( defaultValue == null ) throw makeNoDefaultFail(v, method); + return defaultValue; + } + + int idx = 0; + for ( Object guess : v.valueGuesses ) { + Object result = guess == null ? null : guessToType(guess, expected, v, idx); + if ( !isArray ) { + if ( result == null ) { + Object defaultValue = method.getDefaultValue(); + if ( defaultValue == null ) throw makeNoDefaultFail(v, method); + return defaultValue; + } else return result; + } else { + if ( result == null ) { + if ( v.valueGuesses.size() == 1 ) { + Object defaultValue = method.getDefaultValue(); + if ( defaultValue == null ) throw makeNoDefaultFail(v, method); + return defaultValue; + } else throw new AnnotationValueDecodeFail(v, + "I can't make sense of this annotation value. Try using a fully qualified literal.", idx); + } + Array.set(array, idx++, result); + } + } + + return array; + } + }; + + return (A) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type }, invocations); + } + + private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) { + if ( expected == int.class ) { + if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) { + return ((Number)guess).intValue(); + } + } + + if ( expected == long.class ) { + if ( guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) { + return ((Number)guess).longValue(); + } + } + + if ( expected == short.class ) { + if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) { + int intVal = ((Number)guess).intValue(); + int shortVal = ((Number)guess).shortValue(); + if ( shortVal == intVal ) return shortVal; + } + } + + if ( expected == byte.class ) { + if ( guess instanceof Integer || guess instanceof Short || guess instanceof Byte ) { + int intVal = ((Number)guess).intValue(); + int byteVal = ((Number)guess).byteValue(); + if ( byteVal == intVal ) return byteVal; + } + } + + if ( expected == double.class ) { + if ( guess instanceof Number ) return ((Number)guess).doubleValue(); + } + + if ( expected == float.class ) { + if ( guess instanceof Number ) return ((Number)guess).floatValue(); + } + + if ( expected == boolean.class ) { + if ( guess instanceof Boolean ) return ((Boolean)guess).booleanValue(); + } + + if ( expected == char.class ) { + if ( guess instanceof Character ) return ((Character)guess).charValue(); + } + + if ( expected == String.class ) { + if ( guess instanceof String ) return expected; + } + + if ( Enum.class.isAssignableFrom(expected) ) { + if ( guess instanceof String ) { + for ( Object enumConstant : expected.getEnumConstants() ) { + String target = ((Enum<?>)enumConstant).name(); + if ( target.equals(guess) ) return enumConstant; + } + throw new AnnotationValueDecodeFail(v, + "Can't translate " + guess + " to an enum of type " + expected, pos); + } + } + + if ( Class.class == expected ) { + if ( guess instanceof String ) try { + return Class.forName(toFQ((String)guess)); + } catch ( ClassNotFoundException e ) { + throw new AnnotationValueDecodeFail(v, + "Can't translate " + guess + " to a class object.", pos); + } + } + + throw new AnnotationValueDecodeFail(v, + "Can't translate a " + guess.getClass() + " to the expected " + expected, pos); + } + + public List<String> getRawExpressions(String annotationMethodName) { + AnnotationValue v = values.get(annotationMethodName); + return v == null ? Collections.<String>emptyList() : v.raws; + } + + public String getRawExpression(String annotationMethodName) { + List<String> l = getRawExpressions(annotationMethodName); + return l.isEmpty() ? null : l.get(0); + } + + public List<String> getProbableFQTypes(String annotationMethodName) { + List<String> result = new ArrayList<String>(); + AnnotationValue v = values.get(annotationMethodName); + if ( v == null ) return Collections.emptyList(); + + for ( Object o : v.valueGuesses ) result.add(o == null ? null : toFQ(o.toString())); + return result; + } + + private String toFQ(String typeName) { + Class<?> c; + boolean fqn = typeName.indexOf('.') > -1; + String prefix = fqn ? typeName.substring(0, typeName.indexOf('.')) : typeName; + + for ( String im : ast.getImportStatements() ) { + int idx = im.lastIndexOf('.'); + String simple = im; + if ( idx > -1 ) simple = im.substring(idx+1); + if ( simple.equals(prefix) ) { + return im + typeName.substring(prefix.length()); + } + } + + c = tryClass(typeName); + if ( c != null ) return c.getName(); + + c = tryClass("java.lang." + typeName); + if ( c != null ) return c.getName(); + + //Try star imports + for ( String im : ast.getImportStatements() ) { + if ( im.endsWith(".*") ) { + c = tryClass(im.substring(0, im.length() -1) + typeName); + if ( c != null ) return c.getName(); + } + } + + if ( !fqn ) { + String pkg = ast.getPackageDeclaration(); + if ( pkg != null ) return pkg + "." + typeName; + } + + return null; + } + + private Class<?> tryClass(String name) { + try { + return Class.forName(name); + } catch ( ClassNotFoundException e ) { + return null; + } + } + + public String getProbableFQType(String annotationMethodName) { + List<String> l = getProbableFQTypes(annotationMethodName); + return l.isEmpty() ? null : l.get(0); + } +} diff --git a/src/lombok/eclipse/TypeResolver.java b/src/lombok/core/TypeResolver.java index 41618513..1e356f89 100644 --- a/src/lombok/eclipse/TypeResolver.java +++ b/src/lombok/core/TypeResolver.java @@ -1,44 +1,35 @@ -package lombok.eclipse; +package lombok.core; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import lombok.core.TypeLibrary; -import lombok.eclipse.EclipseAST.Node; - -import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.ast.ImportReference; -import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import lombok.core.AST.Kind; public class TypeResolver { private final TypeLibrary library; private Collection<String> imports; - - public TypeResolver(TypeLibrary library, EclipseAST.Node top) { + public TypeResolver(TypeLibrary library, String packageString, Collection<String> importStrings) { this.library = library; - this.imports = makeImportList((CompilationUnitDeclaration) top.getEclipseNode()); + this.imports = makeImportList(packageString, importStrings); } - private static Collection<String> makeImportList(CompilationUnitDeclaration declaration) { + private static Collection<String> makeImportList(String packageString, Collection<String> importStrings) { Set<String> imports = new HashSet<String>(); - if ( declaration.currentPackage != null ) imports.add(toQualifiedName(declaration.currentPackage.getImportName()) + ".*"); - if ( declaration.imports != null ) for ( ImportReference importStatement : declaration.imports ) { - imports.add(toQualifiedName(importStatement.getImportName())); - } + if ( packageString != null ) imports.add(packageString + ".*"); + imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings); return imports; } - public Collection<String> findTypeMatches(Node context, TypeReference type) { - Collection<String> potentialMatches = library.findCompatible(toQualifiedName(type.getTypeName())); + public Collection<String> findTypeMatches(AST<?>.Node context, String typeRef) { + Collection<String> potentialMatches = library.findCompatible(typeRef); if ( potentialMatches.isEmpty() ) return Collections.emptyList(); - if ( type.getTypeName().length > 1 ) return potentialMatches; - - String simpleName = new String(type.getTypeName()[0]); + int idx = typeRef.indexOf('.'); + if ( idx > -1 ) return potentialMatches; + String simpleName = typeRef.substring(idx+1); //If there's an import statement that explicitly imports a 'Getter' that isn't any of our potentials, return no matches. if ( nameConflictInImportList(simpleName, potentialMatches) ) return Collections.emptyList(); @@ -48,11 +39,11 @@ public class TypeResolver { if ( potentialMatches.isEmpty() ) return Collections.emptyList(); //Find a lexically accessible type of the same simple name in the same Compilation Unit. If it exists: no matches. - Node n = context; + AST<?>.Node n = context; while ( n != null ) { - if ( n.getEclipseNode() instanceof TypeDeclaration ) { - char[] name = ((TypeDeclaration)n.getEclipseNode()).name; - if ( name != null && new String(name).equals(simpleName) ) return Collections.emptyList(); + if ( n.getKind() == Kind.TYPE ) { + String name = n.getName(); + if ( name != null && name.equals(simpleName) ) return Collections.emptyList(); } n = n.up(); } @@ -65,7 +56,7 @@ public class TypeResolver { Set<String> results = new HashSet<String>(); for ( String importedType : imports ) { - Collection<String> reduced = library.findCompatible(importedType); + Collection<String> reduced = new HashSet<String>(library.findCompatible(importedType)); reduced.retainAll(potentialMatches); results.addAll(reduced); } @@ -87,14 +78,4 @@ public class TypeResolver { int idx = typeName.lastIndexOf('.'); return idx == -1 ? typeName : typeName.substring(idx+1); } - - private static String toQualifiedName(char[][] typeName) { - StringBuilder sb = new StringBuilder(); - boolean first = true; - for ( char[] c : typeName ) { - sb.append(first ? "" : ".").append(c); - first = false; - } - return sb.toString(); - } } diff --git a/src/lombok/eclipse/Eclipse.java b/src/lombok/eclipse/Eclipse.java index e1ff95cd..961b9536 100644 --- a/src/lombok/eclipse/Eclipse.java +++ b/src/lombok/eclipse/Eclipse.java @@ -31,4 +31,14 @@ public class Eclipse { log.log(new Status(IStatus.ERROR, bundleName, message, error)); } + + static String toQualifiedName(char[][] typeName) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for ( char[] c : typeName ) { + sb.append(first ? "" : ".").append(c); + first = false; + } + return sb.toString(); + } } diff --git a/src/lombok/eclipse/EclipseAST.java b/src/lombok/eclipse/EclipseAST.java index 3fdd02e4..53a993ec 100644 --- a/src/lombok/eclipse/EclipseAST.java +++ b/src/lombok/eclipse/EclipseAST.java @@ -1,17 +1,13 @@ package lombok.eclipse; -import static lombok.Lombok.sneakyThrow; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import lombok.core.AST; + import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.apt.dispatch.AptProblem; @@ -22,24 +18,44 @@ import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Clinit; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; import org.eclipse.jdt.internal.compiler.util.Util; -public class EclipseAST { +public class EclipseAST extends AST<ASTNode> { + @Override public String getPackageDeclaration() { + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); + ImportReference pkg = cud.currentPackage; + return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName()); + } + + @Override public Collection<String> getImportStatements() { + List<String> imports = new ArrayList<String>(); + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); + if ( cud.imports == null ) return imports; + for ( ImportReference imp : cud.imports ) { + if ( imp == null ) continue; + imports.add(Eclipse.toQualifiedName(imp.getImportName())); + } + + return imports; + } + public void traverse(EclipseASTVisitor visitor) { Node current = top(); - visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.node); + visitor.visitCompilationUnit(current, (CompilationUnitDeclaration)current.get()); traverseChildren(visitor, current); - visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.node); + visitor.endVisitCompilationUnit(current, (CompilationUnitDeclaration)current.get()); } private void traverseChildren(EclipseASTVisitor visitor, Node node) { - for ( Node child : node.children ) { - ASTNode n = child.node; + for ( Node child : node.down() ) { + ASTNode n = child.get(); if ( n instanceof TypeDeclaration ) { visitor.visitType(child, (TypeDeclaration)n); traverseChildren(visitor, child); @@ -57,20 +73,33 @@ public class EclipseAST { visitor.visitMethod(child, (AbstractMethodDeclaration)n); traverseChildren(visitor, child); visitor.endVisitMethod(child, (AbstractMethodDeclaration)n); + } else if ( n instanceof Argument ) { + ASTNode parent = child.up().get(); + AbstractMethodDeclaration method = null; + if ( parent instanceof AbstractMethodDeclaration ) method = (AbstractMethodDeclaration)parent; + else System.out.println("Weird, this isn't a desc of method: " + parent.getClass() + ": " + parent); + visitor.visitMethodArgument(child, (Argument)n, method); + traverseChildren(visitor, child); + visitor.endVisitMethodArgument(child, (Argument)n, method); } else if ( n instanceof LocalDeclaration ) { visitor.visitLocal(child, (LocalDeclaration)n); traverseChildren(visitor, child); visitor.endVisitLocal(child, (LocalDeclaration)n); } else if ( n instanceof Annotation ) { Node parent = child.up(); - if ( parent.node instanceof TypeDeclaration ) - visitor.visitAnnotationOnType((TypeDeclaration)parent.node, child, (Annotation)n); - else if ( parent.node instanceof AbstractMethodDeclaration ) - visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)parent.node, child, (Annotation)n); - else if ( parent.node instanceof FieldDeclaration ) - visitor.visitAnnotationOnField((FieldDeclaration)parent.node, child, (Annotation)n); - else if ( parent.node instanceof LocalDeclaration ) - visitor.visitAnnotationOnLocal((LocalDeclaration)parent.node, child, (Annotation)n); + if ( parent.get() instanceof TypeDeclaration ) + visitor.visitAnnotationOnType((TypeDeclaration)parent.get(), child, (Annotation)n); + else if ( parent.get() instanceof AbstractMethodDeclaration ) + visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)parent.get(), child, (Annotation)n); + else if ( parent.get() instanceof FieldDeclaration ) + visitor.visitAnnotationOnField((FieldDeclaration)parent.get(), child, (Annotation)n); + else if ( parent.get() instanceof Argument ) + visitor.visitAnnotationOnMethodArgument( + (Argument)parent.get(), + (AbstractMethodDeclaration)parent.directUp().get(), + child, (Annotation)n); + else if ( parent.get() instanceof LocalDeclaration ) + visitor.visitAnnotationOnLocal((LocalDeclaration)parent.get(), child, (Annotation)n); } else if ( n instanceof Statement ) { visitor.visitStatement(child, (Statement)n); traverseChildren(visitor, child); @@ -83,16 +112,12 @@ public class EclipseAST { return completeParse; } - public String getFileName() { - return fileName; - } - - public Node top() { - return top; + @Override public Node top() { + return (Node) super.top(); } public Node get(ASTNode node) { - return nodeMap.get(node); + return (Node) super.get(node); } private class ParseProblem { @@ -111,14 +136,14 @@ public class EclipseAST { } void addToCompilationResult() { - addProblemToCompilationResult(getFileName(), (CompilationUnitDeclaration) top().getEclipseNode(), - isWarning, message, node.getEclipseNode(), sourceStart, sourceEnd); + addProblemToCompilationResult(getFileName(), (CompilationUnitDeclaration) top().get(), + isWarning, message, node.get(), sourceStart, sourceEnd); } } - public void propagateProblems() { + private void propagateProblems() { if ( queuedProblems.isEmpty() ) return; - CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().getEclipseNode(); + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); if ( cud.compilationResult == null ) return; for ( ParseProblem problem : queuedProblems ) problem.addToCompilationResult(); queuedProblems.clear(); @@ -152,59 +177,44 @@ public class EclipseAST { ast.compilationResult.record(ecProblem, null); } - public final class Node { - final ASTNode node; - Node parent; - final Collection<Node> children; - boolean handled; - private final boolean isStructurallySignificant; + public final class Node extends AST<ASTNode>.Node { + Node(ASTNode node, Collection<Node> children, Kind kind) { + super(node, children, kind); + } - Node(ASTNode node, Collection<Node> children) { - this.node = node; - this.children = children == null ? Collections.<Node>emptyList() : children; - this.isStructurallySignificant = calculateIsStructurallySignificant(); + @Override public String getName() { + final char[] n; + if ( node instanceof TypeDeclaration ) n = ((TypeDeclaration)node).name; + else if ( node instanceof FieldDeclaration ) n = ((FieldDeclaration)node).name; + else if ( node instanceof AbstractMethodDeclaration ) n = ((AbstractMethodDeclaration)node).selector; + else if ( node instanceof LocalDeclaration ) n = ((LocalDeclaration)node).name; + else n = null; + + return n == null ? null : new String(n); } - public void addError(String message) { - this.addError(message, this.getEclipseNode().sourceStart, this.getEclipseNode().sourceEnd); + @Override public void addError(String message) { + this.addError(message, this.get().sourceStart, this.get().sourceEnd); } public void addError(String message, int sourceStart, int sourceEnd) { addProblem(new ParseProblem(false, message, this, sourceStart, sourceEnd)); } - public void addWarning(String message) { - this.addWarning(message, this.getEclipseNode( |
