diff options
| author | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-17 10:43:39 +0200 |
|---|---|---|
| committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-06-17 10:43:39 +0200 |
| commit | 024d8ffa9801f463fecadd16f42d51bbed46dea7 (patch) | |
| tree | acb0b85f79eafb517e3472bd3d906235d1541ade /src | |
| parent | aa6d2e262f3d6c43f6d89220cdc10c6954bb2bdd (diff) | |
| download | lombok-024d8ffa9801f463fecadd16f42d51bbed46dea7.tar.gz lombok-024d8ffa9801f463fecadd16f42d51bbed46dea7.tar.bz2 lombok-024d8ffa9801f463fecadd16f42d51bbed46dea7.zip | |
Massive refactors. This list isn't complete, but should give you an idea:
A) many things in lombok.eclipse moved to lombok.core to enable reuse with lombok.javac.
B) lombok.javac works now similarly to eclipse's model: We first make big ASTs that are bidirectionally traversable, then we walk through that for annotations.
C) Instead of getting an annotation instance, you now get an object that is more flexible and can e.g. give you class values in an enum as a string instead of a Class object, which may fail if that class isn't on the classpath of lombok.
D) sources to the internal sun classes for javac added to /contrib.
Diffstat (limited to 'src')
20 files changed, 1565 insertions, 714 deletions
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 |
