Massive performance improvements, and a few potentially breaking changes for other lombok plugin developers.

This commit is contained in:
Roel Spilker committed 2013-03-26 02:42:14 +01:00
1 parent 7a50b9a634
commit 9630fc96e8
16 files changed
+599 -253

No files matched your search

+7 -8
View File
@@ -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;
}
+6 -13
View File
@@ -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.
+43
View File
@@ -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);
}
+13 -45
View File
@@ -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);
}
/**
+24 -44
View File
@@ -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);
}
}
+32 -70
View File
@@ -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;
}
}
+7 -16
View File
@@ -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);
}
}
@@ -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;
}
}
+19 -22
View File
@@ -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);
}
}
@@ -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);
}
+16 -16
View File
@@ -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);
}
}
+2 -15
View File
@@ -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. */
+105
View File
@@ -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;
}
}
@@ -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);
}
+188
View File
@@ -0,0 +1,188 @@
/*
* 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.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class ImmutableList<T> implements Iterable<T> {
private Object[] content;
private static final ImmutableList<?> EMPTY = new ImmutableList<Object>(new Object[0]);
@SuppressWarnings("unchecked")
public static <T> ImmutableList<T> of() {
return (ImmutableList<T>) EMPTY;
}
public static <T> ImmutableList<T> of(T a) {
return new ImmutableList<T>(new Object[] {a});
}
public static <T> ImmutableList<T> of(T a, T b) {
return new ImmutableList<T>(new Object[] {a, b});
}
public static <T> ImmutableList<T> of(T a, T b, T c) {
return new ImmutableList<T>(new Object[] {a, b, c});
}
public static <T> ImmutableList<T> of(T a, T b, T c, T d) {
return new ImmutableList<T>(new Object[] {a, b, c, d});
}
public static <T> ImmutableList<T> of(T a, T b, T c, T d, T e) {
return new ImmutableList<T>(new Object[] {a, b, c, d, e});
}
public static <T> ImmutableList<T> of(T a, T b, T c, T d, T e, T f, @SuppressWarnings("unchecked") T... g) {
Object[] rest = g == null ? new Object[] {null} : g;
Object[] val = new Object[rest.length + 6];
System.arraycopy(rest, 0, val, 6, rest.length);
val[0] = a;
val[1] = b;
val[2] = c;
val[3] = d;
val[4] = e;
val[5] = f;
return new ImmutableList<T>(val);
}
public static <T> ImmutableList<T> copyOf(Collection<? extends T> list) {
return new ImmutableList<T>(list.toArray());
}
public static <T> ImmutableList<T> copyOf(Iterable<? extends T> iterable) {
List<T> list = new ArrayList<T>();
for (T o : iterable) list.add(o);
return copyOf(list);
}
private ImmutableList(Object[] content) {
this.content = content;
}
public ImmutableList<T> replaceElementAt(int idx, T newValue) {
Object[] newContent = content.clone();
newContent[idx] = newValue;
return new ImmutableList<T>(newContent);
}
public ImmutableList<T> append(T newValue) {
int len = content.length;
Object[] newContent = new Object[len + 1];
System.arraycopy(content, 0, newContent, 0, len);
newContent[len] = newValue;
return new ImmutableList<T>(newContent);
}
public ImmutableList<T> prepend(T newValue) {
int len = content.length;
Object[] newContent = new Object[len + 1];
System.arraycopy(content, 0, newContent, 1, len);
newContent[0] = newValue;
return new ImmutableList<T>(newContent);
}
public int indexOf(T val) {
int len = content.length;
if (val == null) {
for (int i = 0; i < len; i++) if (content[i] == null) return i;
return -1;
}
for (int i = 0; i < len; i++) if (val.equals(content[i])) return i;
return -1;
}
public ImmutableList<T> removeElement(T val) {
int idx = indexOf(val);
return idx == -1 ? this : removeElementAt(idx);
}
public ImmutableList<T> removeElementAt(int idx) {
int len = content.length;
Object[] newContent = new Object[len - 1];
if (idx > 0) System.arraycopy(content, 0, newContent, 0, idx);
if (idx < len - 1) System.arraycopy(content, idx + 1, newContent, idx, len - idx - 1);
return new ImmutableList<T>(newContent);
}
public boolean isEmpty() {
return content.length == 0;
}
public int size() {
return content.length;
}
@SuppressWarnings("unchecked")
public T get(int idx) {
return (T) content[idx];
}
public boolean contains(T in) {
if (in == null) {
for (Object e : content) if (e == null) return true;
return false;
}
for (Object e : content) if (in.equals(e)) return true;
return false;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
private int idx = 0;
@Override public boolean hasNext() {
return idx < content.length;
}
@SuppressWarnings("unchecked")
@Override public T next() {
if (idx < content.length) return (T) content[idx++];
throw new NoSuchElementException();
}
@Override public void remove() {
throw new UnsupportedOperationException("List is immutable");
}
};
}
@Override public String toString() {
return Arrays.toString(content);
}
@Override public boolean equals(Object obj) {
if (!(obj instanceof ImmutableList)) return false;
if (obj == this) return true;
return Arrays.equals(content, ((ImmutableList<?>) obj).content);
}
@Override public int hashCode() {
return Arrays.hashCode(content);
}
}
+4 -2
View File
@@ -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
@@ -58,7 +58,9 @@ public class Eclipse {
* but we need to deal with it. This turns [[java][lang][String]] into "java.lang.String".
*/
public static String toQualifiedName(char[][] typeName) {
StringBuilder sb = new StringBuilder();
int len = typeName.length - 1;
for (char[] c : typeName) len += c.length;
StringBuilder sb = new StringBuilder(len);
boolean first = true;
for (char[] c : typeName) {
sb.append(first ? "" : ".").append(c);