diff options
author | Roel Spilker <r.spilker@gmail.com> | 2013-03-26 02:42:14 +0100 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2013-03-26 02:42:14 +0100 |
commit | 9630fc96e8382d68505a4cb8ab2ae08aec48e776 (patch) | |
tree | 10a6e50e5d6001cb2dfa6abbf04be870e665f3ff /src/core/lombok | |
parent | 7a50b9a6345de2826a6fc314c8d31e9bfd3fca32 (diff) | |
download | lombok-9630fc96e8382d68505a4cb8ab2ae08aec48e776.tar.gz lombok-9630fc96e8382d68505a4cb8ab2ae08aec48e776.tar.bz2 lombok-9630fc96e8382d68505a4cb8ab2ae08aec48e776.zip |
Massive performance improvements, and a few potentially breaking changes for other lombok plugin developers.
Diffstat (limited to 'src/core/lombok')
-rw-r--r-- | src/core/lombok/core/AST.java | 15 | ||||
-rw-r--r-- | src/core/lombok/core/AnnotationValues.java | 19 | ||||
-rw-r--r-- | src/core/lombok/core/ImportList.java | 43 | ||||
-rw-r--r-- | src/core/lombok/core/LombokNode.java | 58 | ||||
-rw-r--r-- | src/core/lombok/core/TypeLibrary.java | 68 | ||||
-rw-r--r-- | src/core/lombok/core/TypeResolver.java | 102 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseAST.java | 23 | ||||
-rw-r--r-- | src/core/lombok/eclipse/EclipseImportList.java | 131 | ||||
-rw-r--r-- | src/core/lombok/eclipse/HandlerLibrary.java | 41 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java | 2 | ||||
-rw-r--r-- | src/core/lombok/javac/HandlerLibrary.java | 32 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacAST.java | 17 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacImportList.java | 105 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/JavacHandlerUtil.java | 2 |
14 files changed, 407 insertions, 251 deletions
diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index 68f36412..644d9449 100644 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,6 @@ 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; @@ -54,15 +53,15 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, private L top; private final String fileName; private final String packageDeclaration; - private final Collection<String> imports; + private final ImportList imports; Map<N, Void> identityDetector = new IdentityHashMap<N, Void>(); private Map<N, L> nodeMap = new IdentityHashMap<N, L>(); private boolean changed = false; - protected AST(String fileName, String packageDeclaration, Collection<String> imports) { + protected AST(String fileName, String packageDeclaration, ImportList imports) { this.fileName = fileName == null ? "(unknown).java" : fileName; this.packageDeclaration = packageDeclaration; - this.imports = Collections.unmodifiableCollection(new ArrayList<String>(imports)); + this.imports = imports; } public void setChanged() { @@ -96,7 +95,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, * * Example: "java.util.IOException". */ - public final Collection<String> getImportStatements() { + public final ImportList getImportList() { return imports; } @@ -159,8 +158,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, oldChild.parent = targetNode; } - targetNode.children.clear(); - ((List)targetNode.children).addAll(children); + targetNode.children = ImmutableList.copyOf(children); + return targetNode; } diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java index df056dd4..d04797cb 100644 --- a/src/core/lombok/core/AnnotationValues.java +++ b/src/core/lombok/core/AnnotationValues.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -435,23 +435,16 @@ public class AnnotationValues<A extends Annotation> { } /* 2. Walk through non-star imports and search for a match. */ { - for (String im : ast == null ? Collections.<String>emptyList() : ast.getImportStatements()) { - if (im.endsWith(".*")) continue; - int idx = im.lastIndexOf('.'); - String simple = idx == -1 ? im : im.substring(idx+1); - if (simple.equals(prefix)) { - return im + typeName.substring(prefix.length()); - } + if (prefix.equals(typeName)) { + String fqn = ast.getImportList().getFullyQualifiedNameForSimpleName(typeName); + if (fqn != null) return fqn; } } /* 3. Walk through star imports and, if they start with "java.", use Class.forName based resolution. */ { - List<String> imports = ast == null ? Collections.<String>emptyList() : new ArrayList<String>(ast.getImportStatements()); - imports.add("java.lang.*"); - for (String im : imports) { - if (!im.endsWith(".*") || !im.startsWith("java.")) continue; + for (String potential : ast.getImportList().applyNameToStarImports("java", typeName)) { try { - Class<?> c = Class.forName(im.substring(0, im.length()-1) + typeName); + Class<?> c = Class.forName(potential); if (c != null) return c.getName(); } catch (Throwable t) { //Class.forName failed for whatever reason - it most likely does not exist, continue. diff --git a/src/core/lombok/core/ImportList.java b/src/core/lombok/core/ImportList.java new file mode 100644 index 00000000..95e266c4 --- /dev/null +++ b/src/core/lombok/core/ImportList.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core; + +import java.util.Collection; + +public interface ImportList { + /** + * If there is an explicit import of the stated unqualified type name, return that. Otherwise, return null. + */ + String getFullyQualifiedNameForSimpleName(String unqualified); + + /** + * Returns true if the package name is explicitly star-imported, OR the packageName refers to this source file's own package name, OR packageName is 'java.lang'. + */ + boolean hasStarImport(String packageName); + + /** + * Takes all explicit non-static star imports whose first element is equal to {@code startsWith}, replaces the star with {@code unqualified}, and returns these. + */ + Collection<String> applyNameToStarImports(String startsWith, String unqualified); + + String applyUnqualifiedNameToPackage(String unqualified); +} diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index eac59806..588adc55 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,7 +42,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, protected final A ast; protected final Kind kind; protected final N node; - protected final List<L> children; + protected ImmutableList<L> children; protected L parent; /** structurally significant are those nodes that can be annotated in java 1.6 or are method-like toplevels, @@ -62,7 +62,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, this.ast = ast; this.kind = kind; this.node = node; - this.children = children == null ? new ArrayList<L>() : children; + this.children = children != null ? ImmutableList.copyOf(children) : ImmutableList.<L>of(); for (L child : this.children) { child.parent = (L) this; if (!child.isStructurallySignificant) @@ -91,12 +91,12 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, } /** - * Convenient shortcut to the owning ast object's {@code getImportStatements} method. + * Convenient shortcut to the owning ast object's {@code getImportList} method. * - * @see AST#getImportStatements() + * @see AST#getImportList() */ - public Collection<String> getImportStatements() { - return ast.getImportStatements(); + public ImportList getImportList() { + return ast.getImportList(); } /** @@ -120,35 +120,6 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, return node; } - /** - * Replaces the AST node represented by this node object with the provided node. This node must - * have a parent, obviously, for this to work. - * - * Also affects the underlying (Eclipse/javac) AST. - */ - @SuppressWarnings({"rawtypes", "unchecked"}) - public L replaceWith(N newN, Kind newNodeKind) { - ast.setChanged(); - L newNode = ast.buildTree(newN, newNodeKind); - newNode.parent = parent; - for (int i = 0; i < parent.children.size(); i++) { - if (parent.children.get(i) == this) ((List)parent.children).set(i, newNode); - } - - parent.replaceChildNode(get(), newN); - return newNode; - } - - /** - * Replaces the stated node with a new one. The old node must be a direct child of this node. - * - * Also affects the underlying (Eclipse/javac) AST. - */ - public void replaceChildNode(N oldN, N newN) { - ast.setChanged(); - ast.replaceStatementInNode(get(), oldN, newN); - } - public Kind getKind() { return kind; } @@ -204,12 +175,9 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, /** * Returns all children nodes. - * - * A copy is created, so changing the list has no effect. Also, while iterating through this list, - * you may add, remove, or replace children without causing {@code ConcurrentModificationException}s. */ - public Collection<L> down() { - return new ArrayList<L>(children); + public ImmutableList<L> down() { + return children; } /** @@ -235,13 +203,13 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * * Does not change the underlying (javac/Eclipse) AST, only the wrapped view. */ - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({"unchecked"}) public L add(N newChild, Kind newChildKind) { ast.setChanged(); L n = ast.buildTree(newChild, newChildKind); if (n == null) return null; n.parent = (L) this; - ((List)children).add(n); + children = children.append(n); return n; } @@ -267,7 +235,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, for (LombokNode child : children) child.gatherAndRemoveChildren(map); ast.identityDetector.remove(get()); map.put(get(), (L) this); - children.clear(); + children = ImmutableList.of(); ast.getNodeMap().remove(get()); } @@ -278,7 +246,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, */ public void removeChild(L child) { ast.setChanged(); - children.remove(child); + children = children.removeElement(child); } /** diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java index 0e3f7bf4..a89091c4 100644 --- a/src/core/lombok/core/TypeLibrary.java +++ b/src/core/lombok/core/TypeLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,7 @@ */ package lombok.core; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -40,24 +35,23 @@ import java.util.Map; * <ul><li>foo.Spork</li><li>Spork</li><li>foo.*</li></ul> */ public class TypeLibrary { - private final Map<String, List<String>> keyToFqnMap; - private final String singletonValue; - private final List<String> singletonKeys; + private final Map<String, String> unqualifiedToQualifiedMap; + private final String unqualified, qualified; public TypeLibrary() { - keyToFqnMap = new HashMap<String, List<String>>(); - singletonKeys = null; - singletonValue = null; + unqualifiedToQualifiedMap = new HashMap<String, String>(); + unqualified = null; + qualified = null; } private TypeLibrary(String fqnSingleton) { - keyToFqnMap = null; - singletonValue = fqnSingleton; + unqualifiedToQualifiedMap = null; + qualified = fqnSingleton; int idx = fqnSingleton.lastIndexOf('.'); if (idx == -1) { - singletonKeys = Collections.singletonList(fqnSingleton); + unqualified = fqnSingleton; } else { - singletonKeys = Arrays.asList(fqnSingleton, fqnSingleton.substring(idx + 1), fqnSingleton.substring(0, idx) + ".*"); + unqualified = fqnSingleton.substring(idx + 1); } } @@ -71,42 +65,28 @@ public class TypeLibrary { * @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'. */ public void addType(String fullyQualifiedTypeName) { - if (keyToFqnMap == null) throw new IllegalStateException("SingleType library"); + fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", "."); int idx = fullyQualifiedTypeName.lastIndexOf('.'); if (idx == -1) throw new IllegalArgumentException( "Only fully qualified types are allowed (and stuff in the default package is not palatable to us either!)"); + String unqualified = fullyQualifiedTypeName.substring(idx + 1); + if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library"); - fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", "."); - final String simpleName = fullyQualifiedTypeName.substring(idx +1); - final String packageName = fullyQualifiedTypeName.substring(0, idx); - - if (keyToFqnMap.put(fullyQualifiedTypeName, Collections.singletonList(fullyQualifiedTypeName)) != null) return; - - addToMap(simpleName, fullyQualifiedTypeName); - addToMap(packageName + ".*", fullyQualifiedTypeName); - } - - private TypeLibrary addToMap(String keyName, String fullyQualifiedTypeName) { - List<String> list = keyToFqnMap.get(keyName); - if (list == null) { - list = new ArrayList<String>(); - keyToFqnMap.put(keyName, list); - } - - list.add(fullyQualifiedTypeName); - return this; + unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName); + unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName); } /** - * Returns all items in the type library that may be a match to the provided type. + * Translates an unqualified name such as 'String' to 'java.lang.String', _if_ you added 'java.lang.String' to the library via the {@code addType} method. + * Also returns the input if it is equal to a fully qualified name added to this type library. * - * @param typeReference something like 'String', 'java.lang.String', or 'java.lang.*'. - * @return A list of Fully Qualified Names for all types in the library that fit the reference. + * Returns null if it does not match any type in this type library. */ - public Collection<String> findCompatible(String typeReference) { - if (singletonKeys != null) return singletonKeys.contains(typeReference) ? Collections.singletonList(singletonValue) : Collections.<String>emptyList(); - - List<String> result = keyToFqnMap.get(typeReference); - return result == null ? Collections.<String>emptyList() : Collections.unmodifiableList(result); + public String toQualified(String typeReference) { + if (unqualifiedToQualifiedMap == null) { + if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return qualified; + return null; + } + return unqualifiedToQualifiedMap.get(typeReference); } } diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java index 27f0bfc1..e2ba03b5 100644 --- a/src/core/lombok/core/TypeResolver.java +++ b/src/core/lombok/core/TypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,6 @@ */ package lombok.core; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - import lombok.core.AST.Kind; /** @@ -35,60 +30,54 @@ import lombok.core.AST.Kind; * and this importer also can't find inner types from superclasses/interfaces. */ public class TypeResolver { - private Collection<String> imports; + private ImportList imports; /** * Creates a new TypeResolver that can be used to resolve types in a source file with the given package and import statements. */ - public TypeResolver(String packageString, Collection<String> importStrings) { - this.imports = makeImportList(packageString, importStrings); - } - - private static Collection<String> makeImportList(String packageString, Collection<String> importStrings) { - Set<String> imports = new HashSet<String>(); - if (packageString != null) imports.add(packageString + ".*"); - imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings); - imports.add("java.lang.*"); - return imports; + public TypeResolver(ImportList importList) { + this.imports = importList; } +// private static ImportList makeImportList(String packageString, Collection<String> importStrings) { +// Set<String> imports = new HashSet<String>(); +// if (packageString != null) imports.add(packageString + ".*"); +// imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings); +// imports.add("java.lang.*"); +// return imports; +// } +// public boolean typeMatches(LombokNode<?, ?, ?> context, String fqn, String typeRef) { - return !findTypeMatches(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef).isEmpty(); + return typeRefToFullyQualifiedName(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef) != null; } - /** - * Finds type matches for the stated type reference. The provided context is scanned for local type names - * that shadow type names listed in import statements. If such a shadowing occurs, no matches are returned - * for any shadowed types, as you would expect. - */ - public Collection<String> findTypeMatches(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) { + public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) { // When asking if 'Foo' could possibly be referring to 'bar.Baz', the answer is obviously no. - Collection<String> potentialMatches = library.findCompatible(typeRef); - if (potentialMatches.isEmpty()) return Collections.emptyList(); + String qualified = library.toQualified(typeRef); + if (qualified == null) return null; - // If input type appears to be fully qualified, we found a winner. - int idx = typeRef.indexOf('.'); - if (idx > -1) return potentialMatches; + // When asking if 'lombok.Getter' could possibly be referring to 'lombok.Getter', the answer is obviously yes. + if (typeRef.equals(qualified)) return typeRef; - // If there's an import statement that explicitly imports a 'Getter' that isn't any of our potentials, return no matches, - // because if you want to know if 'Foo' could refer to 'bar.Foo' when 'baz.Foo' is explicitly imported, the answer is no. - if (nameConflictInImportList(typeRef, potentialMatches)) return Collections.emptyList(); + // When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes. + String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(typeRef); + if (fromExplicitImport != null) { + // ... and if 'import foobar.Getter;' is in the source file, the answer is no. + return fromExplicitImport.equals(qualified) ? qualified : null; + } - // Check if any of our potentials are even imported in the first place. If not: no matches. - // Note that (ourPackage.*) is added to the imports. - potentialMatches = eliminateImpossibleMatches(potentialMatches, library); - if (potentialMatches.isEmpty()) return Collections.emptyList(); + // When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no. + String pkgName = qualified.substring(0, qualified.length() - typeRef.length() - 1); + if (!imports.hasStarImport(pkgName)) return null; - // Now the hard part - inner classes or method local classes in our own scope. - // For method locals, this refers to any statements that are 'above' the type reference with the same name. - // For inners, this refers to siblings of us or any parent node that are type declarations. + // Now the hard part: Given that there is a star import, 'Getter' most likely refers to 'lombok.Getter', but type shadowing may occur in which case it doesn't. LombokNode<?, ?, ?> n = context; mainLoop: while (n != null) { if (n.getKind() == Kind.TYPE && typeRef.equals(n.getName())) { // Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes. - return Collections.emptyList(); + return null; } if (n.getKind() == Kind.STATEMENT || n.getKind() == Kind.LOCAL) { @@ -99,7 +88,7 @@ public class TypeResolver { for (LombokNode<?, ?, ?> child : newN.down()) { // We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not // anything in the type library we're trying to find, so, no matches. - if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList(); + if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null; if (child == n) break; } } @@ -110,41 +99,14 @@ public class TypeResolver { if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) { for (LombokNode<?, ?, ?> child : n.down()) { // Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes. - if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList(); + if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null; } } n = n.directUp(); } - // No class in this source file is a match, therefore the potential matches found via the import statements must be it. Return those. - return potentialMatches; - } - - private Collection<String> eliminateImpossibleMatches(Collection<String> potentialMatches, TypeLibrary library) { - Set<String> results = new HashSet<String>(); - - for (String importedType : imports) { - Collection<String> reduced = new HashSet<String>(library.findCompatible(importedType)); - reduced.retainAll(potentialMatches); - results.addAll(reduced); - } - - return results; - } - - private boolean nameConflictInImportList(String simpleName, Collection<String> potentialMatches) { - for (String importedType : imports) { - if (!toSimpleName(importedType).equals(simpleName)) continue; - if (potentialMatches.contains(importedType)) continue; - return true; - } - return false; - } - - private static String toSimpleName(String typeName) { - int idx = typeName.lastIndexOf('.'); - return idx == -1 ? typeName : typeName.substring(idx+1); + return qualified; } } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 8ab42140..d1f4ff06 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,7 @@ import java.util.List; import lombok.Lombok; import lombok.core.AST; +import lombok.core.ImmutableList; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; @@ -55,7 +56,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { * @param ast The compilation unit, which serves as the top level node in the tree to be built. */ public EclipseAST(CompilationUnitDeclaration ast) { - super(toFileName(ast), packageDeclaration(ast), imports(ast)); + super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast)); this.compilationUnitDeclaration = ast; setTop(buildCompilationUnit(ast)); this.completeParse = isComplete(ast); @@ -67,18 +68,6 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName()); } - private static Collection<String> imports(CompilationUnitDeclaration cud) { - List<String> imports = new ArrayList<String>(); - if (cud.imports == null) return imports; - for (ImportReference imp : cud.imports) { - if (imp == null) continue; - String qualifiedName = Eclipse.toQualifiedName(imp.getImportName()); - if ((imp.bits & ASTNode.OnDemand) != 0) qualifiedName += ".*"; - imports.add(qualifiedName); - } - return imports; - } - /** * Runs through the entire AST, starting at the compilation unit, calling the provided visitor's visit methods * for each node, depth first. @@ -88,8 +77,10 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { } void traverseChildren(EclipseASTVisitor visitor, EclipseNode node) { - for (EclipseNode child : node.down()) { - child.traverse(visitor); + ImmutableList<EclipseNode> children = node.down(); + int len = children.size(); + for (int i = 0; i < len; i++) { + children.get(i).traverse(visitor); } } diff --git a/src/core/lombok/eclipse/EclipseImportList.java b/src/core/lombok/eclipse/EclipseImportList.java new file mode 100644 index 00000000..264ed91f --- /dev/null +++ b/src/core/lombok/eclipse/EclipseImportList.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse; + +import static lombok.eclipse.Eclipse.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; + +import lombok.core.ImportList; + +public class EclipseImportList implements ImportList { + private ImportReference[] imports; + private ImportReference pkg; + + public EclipseImportList(CompilationUnitDeclaration cud) { + this.pkg = cud.currentPackage; + this.imports = cud.imports; + } + + @Override public String getFullyQualifiedNameForSimpleName(String unqualified) { + if (imports != null) { + outer: + for (ImportReference imp : imports) { + if ((imp.bits & ASTNode.OnDemand) != 0) continue; + char[][] tokens = imp.tokens; + char[] token = tokens.length == 0 ? new char[0] : tokens[tokens.length - 1]; + int len = token.length; + if (len != unqualified.length()) continue; + for (int i = 0; i < len; i++) if (token[i] != unqualified.charAt(i)) continue outer; + return toQualifiedName(tokens); + } + } + return null; + } + + @Override public boolean hasStarImport(String packageName) { + if (isEqual(packageName, pkg)) return true; + if ("java.lang".equals(packageName)) return true; + if (imports != null) for (ImportReference imp : imports) { + if ((imp.bits & ASTNode.OnDemand) == 0) continue; + if (imp.isStatic()) continue; + if (isEqual(packageName, imp)) return true; + } + return false; + } + + private static boolean isEqual(String packageName, ImportReference pkgOrStarImport) { + if (pkgOrStarImport == null || pkgOrStarImport.tokens == null || pkgOrStarImport.tokens.length == 0) return packageName.isEmpty(); + int pos = 0; + int len = packageName.length(); + for (int i = 0; i < pkgOrStarImport.tokens.length; i++) { + if (i != 0) { + if (pos >= len) return false; + if (packageName.charAt(pos++) != '.') return false; + } + for (int j = 0; j < pkgOrStarImport.tokens[i].length; j++) { + if (pos >= len) return false; + if (packageName.charAt(pos++) != pkgOrStarImport.tokens[i][j]) return false; + } + } + return true; + } + + @Override public Collection<String> applyNameToStarImports(String startsWith, String name) { + List<String> out = Collections.emptyList(); + + if (pkg != null && pkg.tokens != null && pkg.tokens.length != 0) { + char[] first = pkg.tokens[0]; + int len = first.length; + boolean match = true; + if (startsWith.length() == len) { + for (int i = 0; match && i < len; i++) { + if (startsWith.charAt(i) != first[i]) match = false; + } + if (match) out.add(toQualifiedName(pkg.tokens) + "." + name); + } + } + + if (imports != null) { + outer: + for (ImportReference imp : imports) { + if ((imp.bits & ASTNode.OnDemand) == 0) continue; + if (imp.isStatic()) continue; + if (imp.tokens == null || imp.tokens.length == 0) continue; + char[] firstToken = imp.tokens[0]; + if (firstToken.length != startsWith.length()) continue; + for (int i = 0; i < firstToken.length; i++) if (startsWith.charAt(i) != firstToken[i]) continue outer; + String fqn = toQualifiedName(imp.tokens) + "." + name; + if (out.isEmpty()) out = Collections.singletonList(fqn); + else if (out.size() == 1) { + out = new ArrayList<String>(out); + out.add(fqn); + } else { + out.add(fqn); + } + } + } + return out; + } + + @Override public String applyUnqualifiedNameToPackage(String unqualified) { + if (pkg == null || pkg.tokens == null || pkg.tokens.length == 0) return unqualified; + return toQualifiedName(pkg.tokens) + "." + unqualified; + } +} diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 56744793..242e923c 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -218,30 +218,27 @@ public class HandlerLibrary { * @param annotation 'node.get()' - convenience parameter. */ public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) { - String pkgName = annotationNode.getPackageDeclaration(); - Collection<String> imports = annotationNode.getImportStatements(); - - TypeResolver resolver = new TypeResolver(pkgName, imports); + TypeResolver resolver = new TypeResolver(annotationNode.getImportList()); TypeReference rawType = annotation.type; if (rawType == null) return; - for (String fqn : resolver.findTypeMatches(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()))) { - AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); - if (container == null) continue; - if (priority != container.getPriority()) continue; - - if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) { - if (needsHandling(annotation)) container.preHandle(annotation, annotationNode); - continue; - } - - try { - if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode); - } catch (AnnotationValueDecodeFail fail) { - fail.owner.setError(fail.getMessage(), fail.idx); - } catch (Throwable t) { - error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); - } + String fqn = resolver.typeRefToFullyQualifiedName(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName())); + if (fqn == null) return; + AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); + if (container == null) return; + if (priority != container.getPriority()) return; + + if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) { + if (needsHandling(annotation)) container.preHandle(annotation, annotationNode); + return; + } + + try { + if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode); + } catch (AnnotationValueDecodeFail fail) { + fail.owner.setError(fail.getMessage(), fail.idx); + } catch (Throwable t) { + error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 78780522..d47b6715 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -290,7 +290,7 @@ public class EclipseHandlerUtil { if (!lastPartA.equals(lastPartB)) return false; String typeName = toQualifiedName(typeRef.getTypeName()); - TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements()); + TypeResolver resolver = new TypeResolver(node.getImportList()); return resolver.typeMatches(node, type.getName(), typeName); } diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index 2be84355..4306b5f2 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2013 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -231,23 +231,23 @@ public class HandlerLibrary { * @param annotation 'node.get()' - convenience parameter. */ public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation, long priority) { - TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements()); + TypeResolver resolver = new TypeResolver(node.getImportList()); String rawType = annotation.annotationType.toString(); - for (String fqn : resolver.findTypeMatches(node, typeLibrary, rawType)) { - AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); - if (container == null) continue; - - try { - if (container.getPriority() == priority) { - if (checkAndSetHandled(annotation)) container.handle(node); - } - } catch (AnnotationValueDecodeFail fail) { - fail.owner.setError(fail.getMessage(), fail.idx); - } catch (Throwable t) { - String sourceName = "(unknown).java"; - if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName(); - javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t); + String fqn = resolver.typeRefToFullyQualifiedName(node, typeLibrary, rawType); + if (fqn == null) return; + AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); + if (container == null) return; + + try { + if (container.getPriority() == priority) { + if (checkAndSetHandled(annotation)) container.handle(node); } + } catch (AnnotationValueDecodeFail fail) { + fail.owner.setError(fail.getMessage(), fail.idx); + } catch (Throwable t) { + String sourceName = "(unknown).java"; + if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName(); + javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t); } } diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 71c17538..93ea9754 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -46,7 +46,6 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; -import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; @@ -78,7 +77,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { * @param top The compilation unit, which serves as the top level node in the tree to be built. */ public JavacAST(Messager messager, Context context, JCCompilationUnit top) { - super(sourceName(top), packageDeclaration(top), imports(top)); + super(sourceName(top), packageDeclaration(top), new JavacImportList(top)); setTop(buildCompilationUnit(top)); this.context = context; this.messager = messager; @@ -98,16 +97,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null; } - private static Collection<String> imports(JCCompilationUnit cu) { - List<String> imports = new ArrayList<String>(); - for (JCTree def : cu.defs) { - if (def instanceof JCImport) { - imports.add(((JCImport)def).qualid.toString()); - } - } - return imports; - } - public Context getContext() { return context; } @@ -121,9 +110,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } void traverseChildren(JavacASTVisitor visitor, JavacNode node) { - for (JavacNode child : new ArrayList<JavacNode>(node.down())) { - child.traverse(visitor); - } + for (JavacNode child : node.down()) child.traverse(visitor); } /** @return A Name object generated for the proper name table belonging to this AST. */ diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java new file mode 100644 index 00000000..fbd4a518 --- /dev/null +++ b/src/core/lombok/javac/JavacImportList.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac; + +import java.util.ArrayList; +import java.util.Collection; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCImport; +import com.sun.tools.javac.util.List; + +import lombok.core.ImportList; + +public class JavacImportList implements ImportList { + private final JCExpression pkg; + private final List<JCTree> defs; + + public JavacImportList(JCCompilationUnit cud) { + this.pkg = cud.pid; + this.defs = cud.defs; + } + + @Override public String getFullyQualifiedNameForSimpleName(String unqualified) { + for (JCTree def : defs) { + if (!(def instanceof JCImport)) continue; + JCTree qual = ((JCImport) def).qualid; + if (!(qual instanceof JCFieldAccess)) continue; + String simpleName = ((JCFieldAccess) qual).name.toString(); + if (simpleName.equals(unqualified)) return qual.toString(); + } + + return null; + } + + @Override public boolean hasStarImport(String packageName) { + if (pkg != null && pkg.toString().equals(packageName)) return true; + if ("java.lang".equals(packageName)) return true; + + for (JCTree def : defs) { + if (!(def instanceof JCImport)) continue; + if (((JCImport) def).staticImport) continue; + JCTree qual = ((JCImport) def).qualid; + if (!(qual instanceof JCFieldAccess)) continue; + String simpleName = ((JCFieldAccess) qual).name.toString(); + if (!"*".equals(simpleName)) continue; + if (packageName.equals(((JCFieldAccess) qual).selected.toString())) return true; + } + + return false; + } + + @Override public Collection<String> applyNameToStarImports(String startsWith, String name) { + ArrayList<String> out = new ArrayList<String>(); + + if (pkg != null && topLevelName(pkg).equals(startsWith)) out.add(pkg.toString() + "." + name); + + for (JCTree def : defs) { + if (!(def instanceof JCImport)) continue; + if (((JCImport) def).staticImport) continue; + JCTree qual = ((JCImport) def).qualid; + if (!(qual instanceof JCFieldAccess)) continue; + String simpleName = ((JCFieldAccess) qual).name.toString(); + if (!"*".equals(simpleName)) continue; + + String topLevelName = topLevelName(qual); + if (topLevelName.equals(startsWith)) { + out.add(((JCFieldAccess) qual).selected.toString() + "." + name); + } + } + + return out; + } + + private String topLevelName(JCTree tree) { + while (tree instanceof JCFieldAccess) tree = ((JCFieldAccess) tree).selected; + return tree.toString(); + } + + @Override public String applyUnqualifiedNameToPackage(String unqualified) { + if (pkg == null) return unqualified; + return pkg.toString() + "." + unqualified; + } +} diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index e79dd5dc..ef1a9f50 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -185,7 +185,7 @@ public class JavacHandlerUtil { public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) { String typeName = typeNode.toString(); - TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements()); + TypeResolver resolver = new TypeResolver(node.getImportList()); return resolver.typeMatches(node, type.getName(), typeName); } |