diff options
-rw-r--r-- | src/lombok/transformations/TypeLibrary.java | 38 | ||||
-rw-r--r-- | src/lombok/transformations/TypeResolver.java | 95 |
2 files changed, 133 insertions, 0 deletions
diff --git a/src/lombok/transformations/TypeLibrary.java b/src/lombok/transformations/TypeLibrary.java new file mode 100644 index 00000000..00ae64b0 --- /dev/null +++ b/src/lombok/transformations/TypeLibrary.java @@ -0,0 +1,38 @@ +package lombok.transformations; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class TypeLibrary { + private final Map<String, Set<String>> simpleToQualifiedMap = new HashMap<String, Set<String>>(); + + public void addType(String fullyQualifiedTypeName) { + 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!)"); + + final String simpleName = fullyQualifiedTypeName.substring(idx +1); + final String packageName = fullyQualifiedTypeName.substring(0, idx); + + if ( simpleToQualifiedMap.put(fullyQualifiedTypeName, Collections.singleton(fullyQualifiedTypeName)) != null ) return; + + addToMap(simpleName, fullyQualifiedTypeName); + addToMap(packageName + ".*", fullyQualifiedTypeName); + } + + private TypeLibrary addToMap(String keyName, String fullyQualifiedTypeName) { + Set<String> existing = simpleToQualifiedMap.get(keyName); + Set<String> set = (existing == null) ? new HashSet<String>() : new HashSet<String>(existing); + set.add(fullyQualifiedTypeName); + simpleToQualifiedMap.put(keyName, Collections.unmodifiableSet(set)); + return this; + } + + public Collection<String> findCompatible(String typeReference) { + return simpleToQualifiedMap.get(typeReference); + } +} diff --git a/src/lombok/transformations/TypeResolver.java b/src/lombok/transformations/TypeResolver.java new file mode 100644 index 00000000..2a012f57 --- /dev/null +++ b/src/lombok/transformations/TypeResolver.java @@ -0,0 +1,95 @@ +package lombok.transformations; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import lombok.eclipse.EclipseAST; + +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 org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.parser.Parser; + +public class TypeResolver { + private final TypeLibrary library; + private final EclipseAST ast; + private Collection<String> imports; + + + public TypeResolver(TypeLibrary library, Parser parser, EclipseAST ast) { + this.library = library; + this.ast = ast; + this.imports = makeImportList((CompilationUnitDeclaration) ast.top().getEclipseNode()); + } + + private static Collection<String> makeImportList(CompilationUnitDeclaration declaration) { + 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())); + } + return imports; + } + + public Collection<String> findTypeMatches(ASTNode context, TypeReference type) { + Collection<String> potentialMatches = library.findCompatible(toQualifiedName(type.getTypeName())); + if ( potentialMatches.isEmpty() ) return Collections.emptyList(); + + if ( type.getTypeName().length > 1 ) return potentialMatches; + + String simpleName = new String(type.getTypeName()[0]); + + //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(); + + //Check if any of our potentials is even imported in the first place. If not: no matches. + potentialMatches = eliminateImpossibleMatches(potentialMatches); + 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. + + + // The potential matches we found by comparing the import statements is our matching set. Return it. + return potentialMatches; + } + + private Collection<String> eliminateImpossibleMatches(Collection<String> potentialMatches) { + Set<String> results = new HashSet<String>(); + + for ( String importedType : imports ) { + Collection<String> reduced = 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); + } + + 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(); + } +} |