package org.bindgen.processor.util; import static org.bindgen.processor.CurrentEnv.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeVariable; /** A utility class for inspecting/transforming class names to the binding version. */ public class ClassName { private final String fullClassNameWithGenerics; public ClassName(String fullClassNameWithGenerics) { if (fullClassNameWithGenerics.contains(">.")) { this.fullClassNameWithGenerics = this.removeRedundantTypeVarsInOuterClasses(fullClassNameWithGenerics); } else { this.fullClassNameWithGenerics = fullClassNameWithGenerics; } } public String get() { return this.fullClassNameWithGenerics; } public String toString() { return this.get(); } /** @return "Type" if the type is "com.app.Type<String, String>" */ public String getSimpleName() { String p = this.getWithoutGenericPart(); int lastDot = p.lastIndexOf('.'); if (lastDot == -1) { return p; } else { return p.substring(lastDot + 1); } } /** @return "com.app" if the type is "com.app.Type<String, String>" */ public String getPackageName() { String p = this.getWithoutGenericPart(); int lastDot = p.lastIndexOf('.'); if (lastDot == -1) { return ""; } else { return p.substring(0, lastDot); } } /** @return ["T", "U"] if the type is "com.app.Type<T extends Foo, U extends Bar>" */ public List<String> getGenericsWithoutBounds() { List<String> args = new ArrayList<String>(); for (TypeVariable tv : (List<TypeVariable>) this.getDeclaredType().getTypeArguments()) { args.add(tv.toString()); } return args; } /** @return ["T extends Foo", "U extends Bar" if the type is "com.app.Type<T extends Foo, U extends Bar>" */ public List<String> getGenericsWithBounds() { List<String> args = new ArrayList<String>(); for (TypeVariable tv : (List<TypeVariable>) this.getDeclaredType().getTypeArguments()) { String arg = tv.toString(); if (!Util.isOfTypeObjectOrNone(tv.getUpperBound())) { arg += " extends " + tv.getUpperBound().toString(); } args.add(arg); } return args; } /** @return "<String, String>" if the type is "com.app.Type<String, String>" or "" if no generics */ public String getGenericPart() { int firstBracket = this.fullClassNameWithGenerics.indexOf("<"); if (firstBracket != -1) { return this.fullClassNameWithGenerics.substring(firstBracket); } return ""; } /** @return "String, String" if the type is "com.app.Type<String, String>" or "" if no generics */ public String getGenericPartWithoutBrackets() { String type = this.getGenericPart(); if ("".equals(type)) { return type; } return type.substring(1, type.length() - 1); } /** @return "com.app.Type" if the type is "com.app.Type<String, String>" */ public String getWithoutGenericPart() { int firstBracket = this.fullClassNameWithGenerics.indexOf("<"); if (firstBracket != -1) { return this.fullClassNameWithGenerics.substring(0, firstBracket); } return this.fullClassNameWithGenerics; } private DeclaredType getDeclaredType() { TypeElement element = getElementUtils().getTypeElement(this.getWithoutGenericPart()); return element == null ? null : (DeclaredType) element.asType(); } // For some reason eclipse started returning java.util.Map.Entry<K, V>'s // TypeMirror.toString as java.util.Map<K, V>.Entry<K, V>. Map having // generics messes up our code generation, so this strips out any generics // that are not on the "last" class. So far this hack seems to work. private String removeRedundantTypeVarsInOuterClasses(String name) { List<Part> generics = new ArrayList<Part>(); // this is awful, but see the test case for why we're counting open/close brackets int lastOpen = 0; int openBrackets = 0; for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); if (c == '<') { openBrackets++; if (openBrackets == 1) { lastOpen = i; } } else if (c == '>') { openBrackets--; if (openBrackets == 0) { generics.add(new Part(lastOpen, i)); } } } Collections.reverse(generics); generics.remove(0); // the last one is okay while (!generics.isEmpty()) { Part p = generics.remove(0); name = name.substring(0, p.begin) + name.substring(p.end + 1); } return name; } private static class Part { int begin; int end; private Part(int begin, int end) { this.begin = begin; this.end = end; } } }