diff options
author | Reinier Zwitserloot <reinier@tipit.to> | 2009-12-17 12:15:01 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-12-17 12:52:53 +0100 |
commit | 31d7d8ec92314e4e571d95d8a71e1e981004ae29 (patch) | |
tree | 337c8dc803d1dafc83e6558467503fc97e2cdcb4 /src | |
parent | b09257768b071abce0cd40e027fd5357502583fd (diff) | |
download | lombok-31d7d8ec92314e4e571d95d8a71e1e981004ae29.tar.gz lombok-31d7d8ec92314e4e571d95d8a71e1e981004ae29.tar.bz2 lombok-31d7d8ec92314e4e571d95d8a71e1e981004ae29.zip |
getProbablyFQType now works much better, especially at finding classes that aren't in rt.jar.
redit to Petr Jiricka for spotting the problems with the earlier version of toFQ.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/lombok/core/AnnotationValues.java | 101 |
1 files changed, 72 insertions, 29 deletions
diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java index 0408de85..85cae5c2 100644 --- a/src/core/lombok/core/AnnotationValues.java +++ b/src/core/lombok/core/AnnotationValues.java @@ -31,6 +31,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import lombok.core.AST.Kind; + /** * Represents a single annotation in a source file and can be used to query the parameters present on it. * @@ -373,47 +375,88 @@ public class AnnotationValues<A extends Annotation> { return l.isEmpty() ? null : l.get(0); } + /* + * Credit goes to Petr Jiricka of Sun for highlighting the problems with the earlier version of this method. + */ private String toFQ(String typeName) { - Class<?> c; - boolean fqn = typeName.indexOf('.') > -1; - String prefix = fqn ? typeName.substring(0, typeName.indexOf('.')) : typeName; + String prefix = typeName.indexOf('.') > -1 ? typeName.substring(0, typeName.indexOf('.')) : typeName; - for (String im : ast.getImportStatements()) { - int idx = im.lastIndexOf('.'); - String simple = im; - if (idx > -1) simple = im.substring(idx+1); - if (simple.equals(prefix)) { - return im + typeName.substring(prefix.length()); + /* 1. Walk through type names in this source file at this level. */ { + LombokNode<?, ?, ?> n = ast; + walkThroughCU: + while (n != null) { + if (n.getKind() == Kind.TYPE) { + String simpleName = n.getName(); + if (prefix.equals(simpleName)) { + //We found a matching type name in the local hierarchy! + List<String> outerNames = new ArrayList<String>(); + while (true) { + n = n.up(); + if (n == null || n.getKind() == Kind.COMPILATION_UNIT) break; + if (n.getKind() == Kind.TYPE) outerNames.add(n.getName()); + //If our type has a parent that isn't either the CompilationUnit or another type, then we are + //a method-local class or an anonymous inner class literal. These technically do have FQNs + //and we may, with a lot of effort, figure out their name, but, that's some fairly horrible code + //style and these methods have 'probable' in their name for a reason. + break walkThroughCU; + } + StringBuilder result = new StringBuilder(); + if (ast.getPackageDeclaration() != null) result.append(ast.getPackageDeclaration()); + if (result.length() > 0) result.append('.'); + Collections.reverse(outerNames); + for (String outerName : outerNames) result.append(outerName).append('.'); + result.append(typeName); + return result.toString(); + } + } + n = n.up(); } } - c = tryClass(typeName); - if (c != null) return c.getName(); - - c = tryClass("java.lang." + typeName); - if (c != null) return c.getName(); + /* 2. Walk through non-star imports and search for a match. */ { + for (String im : 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()); + } + } + } - //Try star imports - for (String im : ast.getImportStatements()) { - if (im.endsWith(".*")) { - c = tryClass(im.substring(0, im.length() -1) + typeName); - if (c != null) return c.getName(); + /* 3. Walk through star imports and, if they start with "java.", use Class.forName based resolution. */ { + for (String im : ast.getImportStatements()) { + if (!im.endsWith(".*") || !im.startsWith("java.")) continue; + try { + Class<?> c = Class.forName(im.substring(0, im.length()-1) + typeName); + if (c != null) return c.getName(); + } catch (Throwable t) { + //Class.forName failed for whatever reason - it most likely does not exist, continue. + } } } - if (!fqn) { - String pkg = ast.getPackageDeclaration(); - if (pkg != null) return pkg + "." + typeName; + /* 4. If the type name is a simple name, then our last guess is that it's another class in this package. */ { + if (typeName.indexOf('.') == -1) return inLocalPackage(ast, typeName); } - return null; + /* 5. It's either an FQN or a nested class in another class in our package. Use code conventions to guess. */ { + char firstChar = typeName.charAt(0); + if (Character.isTitleCase(firstChar) || Character.isUpperCase(firstChar)) { + //Class names start with uppercase letters, so presume it's a nested class in another class in our package. + return inLocalPackage(ast, typeName); + } + + //Presume it's fully qualified. + return typeName; + } } - private Class<?> tryClass(String name) { - try { - return Class.forName(name); - } catch (ClassNotFoundException e) { - return null; - } + private static String inLocalPackage(LombokNode<?, ?, ?> node, String typeName) { + StringBuilder result = new StringBuilder(); + if (node.getPackageDeclaration() != null) result.append(node.getPackageDeclaration()); + if (result.length() > 0) result.append('.'); + result.append(typeName); + return result.toString(); } } |