Temporarily include bndlib 1.47 for testing purposes (not yet on central)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Access.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Access.java
new file mode 100644
index 0000000..ac8bb37
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Access.java
@@ -0,0 +1,25 @@
+package aQute.bnd.compatibility;
+
+import java.lang.reflect.*;
+
+/**
+ * Access modifier
+ */
+public enum Access {
+ PUBLIC, PROTECTED, PACKAGE, PRIVATE, UNKNOWN;
+
+ public static Access modifier(int mod) {
+ if (Modifier.isPublic(mod))
+ return PUBLIC;
+ if (Modifier.isProtected(mod))
+ return PROTECTED;
+ if (Modifier.isPrivate(mod))
+ return PRIVATE;
+
+ return PACKAGE;
+ }
+
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericParameter.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericParameter.java
new file mode 100644
index 0000000..1eac28c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericParameter.java
@@ -0,0 +1,25 @@
+package aQute.bnd.compatibility;
+
+public class GenericParameter {
+ String name;
+ GenericType bounds[];
+
+ public GenericParameter(String name, GenericType[] bounds) {
+ this.name = name;
+ this.bounds = bounds;
+ if (bounds == null || bounds.length == 0)
+ bounds = new GenericType[] { new GenericType( Object.class) };
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(name);
+ if ( bounds != null && bounds.length > 0) {
+ for ( GenericType gtype : bounds ) {
+ sb.append( ":");
+ sb.append(gtype);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java
new file mode 100644
index 0000000..847a358
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/GenericType.java
@@ -0,0 +1,37 @@
+package aQute.bnd.compatibility;
+
+
+public class GenericType {
+ public GenericType(Class<Object> class1) {
+ // TODO Auto-generated constructor stub
+ }
+
+ final static GenericType EMPTY[] = new GenericType[0];
+ Scope reference;
+ GenericType[] a;
+ GenericType[] b;
+ int array;
+
+ Scope scope;
+
+ static public class GenericWildcard extends GenericType{
+
+ public GenericWildcard(Class<Object> class1) {
+ super(class1);
+ // TODO Auto-generated constructor stub
+ }
+
+ }
+
+ static public class GenericArray extends GenericType {
+
+ public GenericArray(Class<Object> class1) {
+ super(class1);
+ // TODO Auto-generated constructor stub
+ }
+
+ }
+
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Kind.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Kind.java
new file mode 100644
index 0000000..1e84030
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Kind.java
@@ -0,0 +1,13 @@
+package aQute.bnd.compatibility;
+
+/**
+ * The kind of thing we scope
+ *
+ */
+public enum Kind {
+ ROOT, CLASS, FIELD, CONSTRUCTOR, METHOD, UNKNOWN;
+
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/ParseSignatureBuilder.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/ParseSignatureBuilder.java
new file mode 100644
index 0000000..dc578ea
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/ParseSignatureBuilder.java
@@ -0,0 +1,109 @@
+package aQute.bnd.compatibility;
+
+import java.io.*;
+
+import aQute.lib.osgi.*;
+
+public class ParseSignatureBuilder {
+ final Scope root;
+
+ public ParseSignatureBuilder(Scope root) {
+ this.root = root;
+ }
+
+ public void add( Jar jar ) throws Exception {
+ for ( Resource r : jar.getResources().values()) {
+ InputStream in = r.openInputStream();
+ try {
+ parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
+ public Scope getRoot() { return root; }
+
+
+ public void parse(InputStream in) throws IOException {
+ Clazz clazz = new Clazz("", null);
+
+ clazz.parseClassFile(in, new ClassDataCollector() {
+ Scope s;
+ Scope enclosing;
+ Scope declaring;
+
+ public void classBegin(int access, String name) {
+ s = root.getScope(Scope.classIdentity(name));
+ s.access = Access.modifier(access);
+ s.kind = Kind.CLASS;
+ }
+
+ public void extendsClass(String name) {
+// s.setBase(new GenericType(name));
+ }
+
+ public void implementsInterfaces(String names[]) {
+ s.setParameterTypes(convert(names));
+ }
+
+ GenericType[] convert(String names[]) {
+ GenericType tss[] = new GenericType[names.length];
+ for (int i = 0; i < names.length; i++) {
+// tss[i] = new GenericType(names[i]);
+ }
+ return tss;
+ }
+
+ public void method(Clazz.MethodDef defined) {
+ String descriptor;
+ Kind kind;
+ if (defined.isConstructor()) {
+ descriptor = ":" + defined.descriptor;
+ kind = Kind.CONSTRUCTOR;
+ } else {
+ descriptor = defined.name + ":" + defined.descriptor;
+ kind = Kind.METHOD;
+ }
+ Scope m = s.getScope(descriptor);
+ m.access = Access.modifier(defined.access);
+ m.kind = kind;
+ m.declaring = s;
+ s.add(m);
+ }
+
+ public void field(Clazz.FieldDef defined) {
+ String descriptor = defined.name + ":" + defined.descriptor;
+ Kind kind = Kind.FIELD;
+ Scope m = s.getScope(descriptor);
+ m.access = Access.modifier(defined.access);
+ m.kind = kind;
+ m.declaring = s;
+ s.add(m);
+ }
+
+ public void classEnd() {
+ if (enclosing != null)
+ s.setEnclosing( enclosing );
+ if (declaring != null)
+ s.setDeclaring( declaring );
+ }
+
+ public void enclosingMethod(String cName, String mName, String mDescriptor) {
+ enclosing = root.getScope(Scope.classIdentity(cName));
+ if (mName != null) {
+ enclosing = enclosing.getScope(Scope.methodIdentity(mName, mDescriptor));
+ }
+ }
+
+ public void innerClass(String innerClass, String outerClass, String innerName,
+ int innerClassAccessFlags) {
+ if (outerClass != null && innerClass != null && innerClass.equals(s.name))
+ declaring = root.getScope(Scope.classIdentity(outerClass));
+ }
+ });
+
+
+ }
+}
+
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/RuntimeSignatureBuilder.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/RuntimeSignatureBuilder.java
new file mode 100644
index 0000000..7761701
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/RuntimeSignatureBuilder.java
@@ -0,0 +1,220 @@
+package aQute.bnd.compatibility;
+
+import java.lang.reflect.*;
+
+public class RuntimeSignatureBuilder {
+ final Scope root;
+
+ public RuntimeSignatureBuilder(Scope root) {
+ this.root = root;
+ }
+
+ static public String identity(Class<?> c) {
+ return Scope.classIdentity(c.getName());
+ }
+
+ static public String identity(Method m) {
+ return Scope.methodIdentity(m.getName(), getDescriptor(m.getReturnType(), m
+ .getParameterTypes()));
+ }
+
+ static public String identity(Constructor m) {
+ return Scope.constructorIdentity(getDescriptor(void.class, m.getParameterTypes()));
+ }
+
+ static public String identity(Field m) {
+ return Scope.fieldIdentity(m.getName(), getDescriptor(m.getType(), null));
+ }
+
+ static public String getDescriptor(Class<?> base, Class<?>[] parameters) {
+ StringBuilder sb = new StringBuilder();
+ if (parameters != null) {
+ sb.append("(");
+ for (Class<?> parameter : parameters) {
+ sb.append(getDescriptor(parameter));
+ }
+ sb.append(")");
+ }
+ sb.append(getDescriptor(base));
+ return sb.toString();
+ }
+
+ public Scope add(Class<?> c) {
+ Scope local = add(root, getEnclosingScope(c), c.getModifiers(), c.getTypeParameters(),
+ Kind.CLASS, identity(c), c.getGenericSuperclass(), c.getGenericInterfaces(), null);
+
+ for (Field f : c.getDeclaredFields()) {
+ add(local, // declaring scope
+ local, // enclosing
+ f.getModifiers(), // access modifiers
+ null, // fields have no type vars
+ Kind.FIELD, // field
+ identity(f), // the name of the field
+ f.getGenericType(), // the type of the field
+ null, // fields have no parameters
+ null // fields have no exceptions
+ );
+ }
+
+ for (Constructor constr : c.getConstructors()) {
+ add(local, // class scope
+ local, // enclosing
+ constr.getModifiers(), // access modifiers
+ constr.getTypeParameters(), // Type vars
+ Kind.CONSTRUCTOR, // constructor
+ identity(constr), // <init>(type*)
+ void.class, // Always void
+ constr.getGenericParameterTypes(), // parameters types
+ constr.getGenericExceptionTypes() // exception types
+ );
+ }
+
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.getDeclaringClass() != Object.class) {
+ add(local, // class scope
+ local, // enclosing
+ m.getModifiers(), // access modifiers
+ m.getTypeParameters(), Kind.METHOD, // method
+ identity(m), // <name>(type*)return
+ m.getGenericReturnType(), // return type
+ m.getGenericParameterTypes(), // parameter types
+ m.getGenericExceptionTypes() // exception types
+ );
+ }
+ }
+
+ return local;
+ }
+
+ private Scope getEnclosingScope(Class<?> c) {
+ Method m = c.getEnclosingMethod();
+ if (m != null) {
+ Scope s = getGlobalScope(m.getDeclaringClass());
+ return s.getScope(identity(m));
+ }
+// TODO
+// Constructor cnstr = c.getEnclosingConstructor();
+// if (m != null) {
+// Scope s = getGlobalScope(cnstr.getDeclaringClass());
+// return s.getScope(identity(cnstr));
+//
+// }
+ Class<?> enclosingClass = c.getEnclosingClass();
+ if (enclosingClass != null) {
+ return getGlobalScope(enclosingClass);
+ }
+
+ return null;
+ }
+
+ private Scope getGlobalScope(Class<?> c) {
+ if (c == null)
+ return null;
+ String id = identity(c);
+ return root.getScope(id);
+ }
+
+ private Scope add(Scope declaring, Scope enclosing, int modifiers,
+ TypeVariable<?>[] typeVariables, Kind kind, String id, Type mainType,
+ Type[] parameterTypes, Type exceptionTypes[]) {
+
+ Scope scope = declaring.getScope(id);
+ assert scope.access == Access.UNKNOWN;
+ scope.setAccess(Access.modifier(modifiers));
+ scope.setKind(kind);
+ scope.setGenericParameter(convert(typeVariables));
+ scope.setBase(convert(scope,mainType));
+ scope.setParameterTypes(convert(parameterTypes));
+ scope.setExceptionTypes(convert(exceptionTypes));
+ scope.setDeclaring(declaring);
+ scope.setEnclosing(enclosing);
+ return scope;
+ }
+
+ private GenericType convert(Scope source, Type t) {
+ if (t instanceof ParameterizedType) {
+ // C<P..>
+ ParameterizedType pt = (ParameterizedType) t;
+ /*Scope reference =*/ root.getScope(identity((Class<?>)pt.getRawType()));
+ Type args[] = pt.getActualTypeArguments();
+ GenericType[] arguments = new GenericType[args.length];
+ int n = 0;
+ for (Type arg : args)
+ arguments[n++] = convert(source,arg);
+// return new GenericType(reference,null,arguments);
+
+ } else if (t instanceof TypeVariable) {
+// TypeVariable tv = (TypeVariable) t;
+// return new GenericType(source,tv.getName(), null);
+ } else if (t instanceof WildcardType) {
+// WildcardType wc = (WildcardType) t;
+// wc.
+ } else if (t instanceof GenericArrayType) {
+
+ }
+ if (t instanceof Class<?>) {
+// raw = ((Class<?>) t).getName() + ";";
+ } else
+ throw new IllegalArgumentException(t.toString());
+
+ return null;
+ }
+
+ private GenericParameter[] convert(TypeVariable vars[]) {
+ if (vars == null)
+ return null;
+
+ GenericParameter out[] = new GenericParameter[vars.length];
+ for (int i = 0; i < vars.length; i++) {
+ GenericType gss[] = convert(vars[i].getBounds());
+ out[i] = new GenericParameter(vars[i].getName(), gss);
+ }
+ return out;
+ }
+
+ private GenericType[] convert(Type[] parameterTypes) {
+ if (parameterTypes == null || parameterTypes.length == 0)
+ return GenericType.EMPTY;
+
+ GenericType tss[] = new GenericType[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ //tss[i] = new GenericType(parameterTypes[i]);
+ }
+ return tss;
+ }
+
+ private static String getDescriptor(Class<?> c) {
+ StringBuilder sb = new StringBuilder();
+ if (c.isPrimitive()) {
+ if (c == boolean.class)
+ sb.append("Z");
+ else if (c == byte.class)
+ sb.append("Z");
+ else if (c == char.class)
+ sb.append("C");
+ else if (c == short.class)
+ sb.append("S");
+ else if (c == int.class)
+ sb.append("I");
+ else if (c == long.class)
+ sb.append("J");
+ else if (c == float.class)
+ sb.append("F");
+ else if (c == double.class)
+ sb.append("D");
+ else if (c == void.class)
+ sb.append("V");
+ else
+ throw new IllegalArgumentException("unknown primitive type: " + c);
+ } else if (c.isArray()) {
+ sb.append("[");
+ sb.append(getDescriptor(c));
+ } else {
+ sb.append("L");
+ sb.append(c.getName().replace('.', '/'));
+ sb.append(";");
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Scope.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Scope.java
new file mode 100644
index 0000000..218a31c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Scope.java
@@ -0,0 +1,166 @@
+package aQute.bnd.compatibility;
+
+import java.io.*;
+import java.util.*;
+
+public class Scope {
+ final Map<String, Scope> children = new LinkedHashMap<String, Scope>();
+
+ // class: slashed name
+ // field: name ":" typed
+ // constructor: ":(" typed* ")" typed
+ // method: name ":(" typed* ")" typed
+ final String name;
+
+ Access access;
+ Kind kind;
+ Scope enclosing;
+ Scope declaring;
+ GenericParameter typeVars[];
+ Map<String, String[]> name2bounds;
+
+ // class: super
+ // field: type
+ // constructor: void
+ // method: return
+ GenericType base;
+
+ // class: interfaces
+ // constructor: args
+ // method: args
+ GenericType[] parameters;
+
+ // constructor: exceptions
+ // method: exceptions
+ GenericType[] exceptions;
+
+ // class: super interfaces*
+ // field: type
+ // constructor: void arguments*
+ // method: return arguments*
+
+ public Scope(Access access, Kind kind, String name) {
+ this.access = access;
+ this.kind = kind;
+ this.name = name;
+ }
+
+ Scope getScope(String name) {
+ Scope s = children.get(name);
+ if (s != null)
+ return s;
+
+ s = new Scope(Access.UNKNOWN, Kind.UNKNOWN, name);
+ children.put(name, s);
+ s.declaring = this;
+ return s;
+ }
+
+ public void setParameterTypes(GenericType[] convert) {
+ this.parameters = convert;
+ }
+
+ public void setExceptionTypes(GenericType[] convert) {
+ this.exceptions = convert;
+ }
+
+ public void setBase(GenericType typeSignature) {
+ base = typeSignature;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ if ( typeVars != null && typeVars.length !=0) {
+ sb.append("<");
+ for ( GenericParameter v : typeVars) {
+ sb.append(v);
+ }
+ sb.append(">");
+ }
+ sb.append(access.toString());
+ sb.append(" ");
+ sb.append(kind.toString());
+ sb.append( " ");
+ sb.append( name );
+ return sb.toString();
+ }
+
+ public void report(Appendable a, int indent) throws IOException {
+ for (int i = 0; i < indent; i++)
+ a.append(" ");
+ a.append(toString());
+ a.append("\n");
+ for (Scope s : children.values())
+ s.report(a, indent + 1);
+ }
+
+ public void add(Scope m) {
+ children.put(m.name, m);
+
+ }
+
+ public void setDeclaring(Scope declaring) {
+ this.declaring = declaring;
+ }
+
+ public void setAccess(Access access) {
+ this.access = access;
+ }
+
+ public void setEnclosing(Scope enclosing) {
+ this.enclosing = enclosing;
+ if (this.enclosing != null) {
+ this.enclosing.add(this);
+ }
+ }
+
+ public boolean isTop() {
+ return enclosing == null;
+ }
+
+ public void setKind(Kind kind) {
+ this.kind = kind;
+ }
+
+ static public String classIdentity(String name) {
+ return name.replace('.', '/');
+ }
+
+ static public String methodIdentity(String name, String descriptor) {
+ return name + ":" + descriptor;
+ }
+
+ static public String constructorIdentity(String descriptor) {
+ return ":" + descriptor;
+ }
+
+ static public String fieldIdentity(String name, String descriptor) {
+ return name + ":" + descriptor;
+ }
+
+ public void cleanRoot() {
+ Iterator<Map.Entry<String, Scope>> i = children.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String, Scope> entry = i.next();
+ if (!entry.getValue().isTop())
+ i.remove();
+ }
+ }
+
+ public void prune(EnumSet<Access> level) {
+ Iterator<Map.Entry<String, Scope>> i = children.entrySet().iterator();
+ while (i.hasNext()) {
+ Map.Entry<String, Scope> entry = i.next();
+ if (!level.contains(entry.getValue().access))
+ i.remove();
+ else
+ entry.getValue().prune(level);
+ }
+ }
+
+ public void setGenericParameter(GenericParameter[] typeVars) {
+ this.typeVars = typeVars;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java
new file mode 100644
index 0000000..25177df
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/SignatureGenerator.java
@@ -0,0 +1,125 @@
+package aQute.bnd.compatibility;
+
+
+public class SignatureGenerator {
+// enum ACCESS {
+//// PUBLIC("+"), PROTECTED("|"), PACKAGE_PRIVATE(""), PRIVATE("-");
+//// final String repr;
+////
+//// ACCESS(String s) {
+//// repr = s;
+//// }
+////
+//// public String toString() {
+//// return repr;
+//// }
+// }
+//
+// public static void main(String args[]) throws Exception {
+// final PrintStream out = System.out;
+//
+// Clazz c = new Clazz("x", new FileResource(new File(
+// "src/aQute/bnd/compatibility/SignatureGenerator.class")));
+// c.parseClassFileWithCollector(new ClassDataCollector() {
+// public void classBegin(int access, String name) {
+// out.print(name);
+// out.println(access(access));
+// }
+//
+// private ACCESS access(int access) {
+// if (Modifier.isPublic(access))
+// return ACCESS.PUBLIC;
+//
+// throw new IllegalArgumentException();
+// }
+//
+// public void extendsClass(String name) {
+// }
+//
+// public void implementsInterfaces(String name[]) {
+// }
+//
+// public void addReference(String token) {
+// }
+//
+// public void annotation(Annotation annotation) {
+// }
+//
+// public void parameter(int p) {
+// }
+//
+// public void method(Clazz.MethodDef defined) {
+// if (defined.isConstructor())
+// constructor(defined.access, defined.descriptor);
+// else
+// method(defined.access, defined.name, defined.descriptor);
+// }
+//
+// public void field(Clazz.FieldDef defined) {
+// field(defined.access, defined.name, defined.descriptor);
+// }
+//
+// public void reference(Clazz.MethodDef referenced) {
+// }
+//
+// public void reference(Clazz.FieldDef referenced) {
+// }
+//
+// public void classEnd() {
+// }
+//
+// @Deprecated// Will really be removed!
+// public void field(int access, String name, String descriptor) {
+// }
+//
+// @Deprecated// Will really be removed!
+// public void constructor(int access, String descriptor) {
+// }
+//
+// @Deprecated// Will really be removed!
+// public void method(int access, String name, String descriptor) {
+// }
+//
+// /**
+// * The EnclosingMethod attribute
+// *
+// * @param cName
+// * The name of the enclosing class, never null. Name is
+// * with slashes.
+// * @param mName
+// * The name of the enclosing method in the class with
+// * cName or null
+// * @param mDescriptor
+// * The descriptor of this type
+// */
+// public void enclosingMethod(String cName, String mName, String mDescriptor) {
+//
+// }
+//
+// /**
+// * The InnerClass attribute
+// *
+// * @param innerClass
+// * The name of the inner class (with slashes). Can be
+// * null.
+// * @param outerClass
+// * The name of the outer class (with slashes) Can be
+// * null.
+// * @param innerName
+// * The name inside the outer class, can be null.
+// * @param modifiers
+// * The access flags
+// */
+// public void innerClass(String innerClass, String outerClass, String innerName,
+// int innerClassAccessFlags) {
+// }
+//
+// public void signature(String signature) {
+// }
+//
+// public void constant(Object object) {
+// }
+//
+// });
+// }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
new file mode 100644
index 0000000..ec4a545
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/compatibility/Signatures.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package aQute.bnd.compatibility;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * This class is compiled against 1.5 or later to provide access to the generic
+ * signatures. It can convert a Class, Field, Method or constructor to a generic
+ * signature and it can normalize a signature. Both are methods. Normalized
+ * signatures can be string compared and match even if the type variable names
+ * differ.
+ *
+ * @version $Id$
+ */
+public class Signatures {
+
+
+ /**
+ * Check if the environment has generics, i.e. later than
+ * Java 5 VM.
+ *
+ * @return true if generics are supported
+ * @throws Exception
+ */
+ public boolean hasGenerics() throws Exception {
+ try {
+ call( Signatures.class, "getGenericSuperClass");
+ return true;
+ } catch( NoSuchMethodException mnfe ) {
+ return false;
+ }
+ }
+
+
+
+ /**
+ * Helper class to track an index in a string.
+ */
+ class Rover {
+ final String s;
+ int i;
+
+ public Rover(String s) {
+ this.s = s;
+ i = 0;
+ }
+
+ char peek() {
+ return s.charAt(i);
+ }
+
+ char take() {
+ return s.charAt(i++);
+ }
+
+ char take(char c) {
+ char x = s.charAt(i++);
+ if (c != x)
+ throw new IllegalStateException("get() expected " + c
+ + " but got + " + x);
+ return x;
+ }
+
+ public String upTo(String except) {
+ int start = i;
+ while (except.indexOf(peek()) < 0)
+ take();
+ return s.substring(start, i);
+ }
+
+ public boolean isEOF() {
+ return i >= s.length();
+ }
+
+ }
+
+ /**
+ * Calculate the generic signature of a Class,Method,Field, or Constructor.
+ * @param f
+ * @return
+ * @throws Exception
+ */
+ public String getSignature(Object c) throws Exception {
+ if( c instanceof Class<?>)
+ return getSignature((Class<?>)c);
+ if( c instanceof Constructor<?>)
+ return getSignature((Constructor<?>)c);
+ if( c instanceof Method)
+ return getSignature((Method)c);
+ if( c instanceof Field)
+ return getSignature((Field)c);
+
+ throw new IllegalArgumentException(c.toString());
+ }
+
+ /**
+ * Calculate the generic signature of a Class. A Class consists of:
+ *
+ * <pre>
+ * class ::= declaration? reference reference*
+ * </pre>
+ *
+ *
+ * @param f
+ * @return
+ * @throws Exception
+ */
+ public String getSignature(Class< ? > c) throws Exception {
+ StringBuffer sb = new StringBuffer();
+ declaration(sb, c);
+ reference(sb, call(c, "getGenericSuperclass"));
+ for (Object type : (Object[]) call(c,"getGenericInterfaces")) {
+ reference(sb, type);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Calculate the generic signature of a Method. A Method consists of:
+ *
+ * <pre>
+ * method ::= declaration? '(' reference* ')' reference
+ * </pre>
+ *
+ * @param c
+ * @return
+ * @throws Exception
+ */
+ public String getSignature(Method m) throws Exception {
+ StringBuffer sb = new StringBuffer();
+ declaration(sb, m);
+ sb.append('(');
+ for (Object type : (Object[]) call(m,"getGenericParameterTypes")) {
+ reference(sb, type);
+ }
+ sb.append(')');
+ reference(sb, call(m,"getGenericReturnType"));
+ return sb.toString();
+ }
+
+ /**
+ * Calculate the generic signature of a Constructor. A Constructor consists
+ * of:
+ *
+ * <pre>
+ * constructor ::= declaration? '(' reference* ')V'
+ * </pre>
+ *
+ * @param c
+ * @return
+ * @throws Exception
+ */
+ public String getSignature(Constructor< ? > c) throws Exception {
+ StringBuffer sb = new StringBuffer();
+ declaration(sb, c);
+ sb.append('(');
+ for (Object type : (Object[]) call(c,"getGenericParameterTypes")) {
+ reference(sb, type);
+ }
+ sb.append(')');
+ reference(sb, void.class);
+ return sb.toString();
+ }
+
+ /**
+ * Calculate the generic signature of a Field. A Field consists of:
+ *
+ * <pre>
+ * constructor ::= reference
+ * </pre>
+ *
+ * @param c
+ * @return
+ * @throws Exception
+ */
+ public String getSignature(Field f) throws Exception {
+ StringBuffer sb = new StringBuffer();
+ Object t = call(f,"getGenericType");
+ reference(sb, t);
+ return sb.toString();
+ }
+
+/**
+ * Classes, Methods, or Constructors can have a declaration that provides
+ * nested a scope for type variables. A Method/Constructor inherits
+ * the type variables from its class and a class inherits its type variables
+ * from its outer class. The declaration consists of the following
+ * syntax:
+ * <pre>
+ * declarations ::= '<' declaration ( ',' declaration )* '>'
+ * declaration ::= identifier ':' declare
+ * declare ::= types | variable
+ * types ::= ( 'L' class ';' )? ( ':' 'L' interface ';' )*
+ * variable ::= 'T' id ';'
+ * </pre>
+ *
+ * @param sb
+ * @param gd
+ * @throws Exception
+ */
+ private void declaration(StringBuffer sb, Object gd) throws Exception {
+ Object[] typeParameters = (Object[]) call(gd,"getTypeParameters");
+ if (typeParameters.length > 0) {
+ sb.append('<');
+ for (Object tv : typeParameters) {
+ sb.append( call(tv,"getName"));
+
+ Object[] bounds = (Object[]) call(tv,"getBounds");
+ if (bounds.length > 0 && isInterface(bounds[0])) {
+ sb.append(':');
+ }
+ for (int i = 0; i < bounds.length; i++) {
+ sb.append(':');
+ reference(sb, bounds[i]);
+ }
+ }
+ sb.append('>');
+ }
+ }
+
+ /**
+ * Verify that the type is an interface.
+ *
+ * @param type the type to check.
+ * @return true if this is a class that is an interface or a Parameterized
+ * Type that is an interface
+ * @throws Exception
+ */
+ private boolean isInterface(Object type) throws Exception {
+ if (type instanceof Class)
+ return (((Class< ? >) type).isInterface());
+
+ if ( isInstance(type.getClass(), "java.lang.reflect.ParameterizedType"))
+ return isInterface(call(type,"getRawType"));
+
+ return false;
+ }
+
+
+/**
+ * This is the heart of the signature builder. A reference is used
+ * in a lot of places. It referes to another type.
+ * <pre>
+ * reference ::= array | class | primitive | variable
+ * array ::= '[' reference
+ * class ::= 'L' body ( '.' body )* ';'
+ * body ::= id ( '<' ( wildcard | reference )* '>' )?
+ * variable ::= 'T' id ';'
+ * primitive ::= PRIMITIVE
+ * </pre>
+ *
+ * @param sb
+ * @param t
+ * @throws Exception
+ */
+ private void reference(StringBuffer sb, Object t) throws Exception {
+
+ if ( isInstance(t.getClass(),"java.lang.reflect.ParameterizedType")) {
+ sb.append('L');
+ parameterizedType(sb, t);
+ sb.append(';');
+ return;
+ }
+ else
+ if ( isInstance(t.getClass(), "java.lang.reflect.GenericArrayType")) {
+ sb.append('[');
+ reference(sb, call(t,"getGenericComponentType"));
+ }
+ else
+ if ( isInstance(t.getClass(), "java.lang.reflect.WildcardType")) {
+ Object[] lowerBounds = (Object[]) call(t, "getLowerBounds");
+ Object[] upperBounds = (Object[]) call(t, "getUpperBounds");
+
+ if (upperBounds.length == 1
+ && upperBounds[0] == Object.class)
+ upperBounds = new Object[0];
+
+ if (upperBounds.length != 0) {
+ // extend
+ for (Object upper : upperBounds) {
+ sb.append('+');
+ reference(sb, upper);
+ }
+ }
+ else
+ if (lowerBounds.length != 0) {
+ // super, can only be one by the language
+ for (Object lower : lowerBounds) {
+ sb.append('-');
+ reference(sb, lower);
+ }
+ }
+ else
+ sb.append('*');
+ }
+ else
+ if ( isInstance(t.getClass(),"java.lang.reflect.TypeVariable")) {
+ sb.append('T');
+ sb.append( call(t,"getName"));
+ sb.append(';');
+ }
+ else
+ if (t instanceof Class< ? >) {
+ Class< ? > c = (Class< ? >) t;
+ if (c.isPrimitive()) {
+ sb.append(primitive(c));
+ }
+ else {
+ sb.append('L');
+ String name = c.getName().replace('.', '/');
+ sb.append(name);
+ sb.append(';');
+ }
+ }
+ }
+
+ /**
+ * Creates the signature for a Parameterized Type.
+ *
+ * A Parameterized Type has a raw class and a set of type variables.
+ *
+ * @param sb
+ * @param pt
+ * @throws Exception
+ */
+ private void parameterizedType(StringBuffer sb, Object pt) throws Exception {
+ Object owner = call(pt,"getOwnerType");
+ String name = ((Class< ? >) call(pt,"getRawType")).getName()
+ .replace('.', '/');
+ if (owner != null) {
+ if ( isInstance(owner.getClass(), "java.lang.reflect.ParameterizedType"))
+ parameterizedType(sb, owner);
+ else
+ sb.append(((Class< ? >) owner).getName().replace('.', '/'));
+ sb.append('.');
+ int n = name.lastIndexOf('$');
+ name = name.substring(n + 1);
+ }
+ sb.append(name);
+
+ sb.append('<');
+ for (Object parameterType : (Object[]) call(pt,"getActualTypeArguments")) {
+ reference(sb, parameterType);
+ }
+ sb.append('>');
+
+ }
+
+ /**
+ * Handle primitives, these need to be translated to a single char.
+ *
+ * @param type the primitive class
+ * @return the single char associated with the primitive
+ */
+ private char primitive(Class< ? > type) {
+ if (type == byte.class)
+ return 'B';
+ else
+ if (type == char.class)
+ return 'C';
+ else
+ if (type == double.class)
+ return 'D';
+ else
+ if (type == float.class)
+ return 'F';
+ else
+ if (type == int.class)
+ return 'I';
+ else
+ if (type == long.class)
+ return 'J';
+ else
+ if (type == short.class)
+ return 'S';
+ else
+ if (type == boolean.class)
+ return 'Z';
+ else
+ if (type == void.class)
+ return 'V';
+ else
+ throw new IllegalArgumentException(
+ "Unknown primitive type "
+ + type);
+ }
+
+ /**
+ * Normalize a signature to make sure the name of the variables are always
+ * the same. We change the names of the type variables to _n, where n is an
+ * integer. n is incremented for every new name and already used names are
+ * replaced with the _n name.
+ *
+ * @return a normalized signature
+ */
+
+ public String normalize(String signature) {
+ StringBuffer sb = new StringBuffer();
+ Map<String, String> map = new HashMap<String, String>();
+ Rover rover = new Rover(signature);
+ declare(sb, map, rover);
+
+ if (rover.peek() == '(') {
+ // method or constructor
+ sb.append(rover.take('('));
+ while (rover.peek() != ')') {
+ reference(sb, map, rover, true);
+ }
+ sb.append(rover.take(')'));
+ reference(sb, map, rover, true); // return type
+ }
+ else {
+ // field or class
+ reference(sb, map, rover, true); // field type or super class
+ while (!rover.isEOF()) {
+ reference(sb, map, rover, true); // interfaces
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * The heart of the routine. Handle a reference to a type. Can be
+ * an array, a class, a type variable, or a primitive.
+ *
+ * @param sb
+ * @param map
+ * @param rover
+ * @param primitivesAllowed
+ */
+ private void reference(StringBuffer sb, Map<String, String> map,
+ Rover rover, boolean primitivesAllowed) {
+
+ char type = rover.take();
+ sb.append(type);
+
+ if (type == '[') {
+ reference(sb, map, rover, true);
+ }
+ else
+ if (type == 'L') {
+ String fqnb = rover.upTo("<;.");
+ sb.append(fqnb);
+ body(sb, map, rover);
+ while (rover.peek() == '.') {
+ sb.append(rover.take('.'));
+ sb.append(rover.upTo("<;."));
+ body(sb, map, rover);
+ }
+ sb.append(rover.take(';'));
+ }
+ else
+ if (type == 'T') {
+ String name = rover.upTo(";");
+ name = assign(map, name);
+ sb.append(name);
+ sb.append(rover.take(';'));
+ }
+ else {
+ if (!primitivesAllowed)
+ throw new IllegalStateException(
+ "Primitives are not allowed without an array");
+ }
+ }
+
+ /**
+ * Because classes can be nested the body handles the part that can
+ * be nested, the reference handles the enclosing L ... ;
+ *
+ * @param sb
+ * @param map
+ * @param rover
+ */
+ private void body(StringBuffer sb, Map<String, String> map, Rover rover) {
+ if (rover.peek() == '<') {
+ sb.append(rover.take('<'));
+ while (rover.peek() != '>') {
+ switch (rover.peek()) {
+ case 'L' :
+ case '[' :
+ reference(sb, map, rover, false);
+ break;
+
+ case 'T' :
+ String name;
+ sb.append(rover.take('T')); // 'T'
+ name = rover.upTo(";");
+ sb.append(assign(map, name));
+ sb.append(rover.take(';'));
+ break;
+
+ case '+' : // extends
+ case '-' : // super
+ sb.append(rover.take());
+ reference(sb, map, rover, false);
+ break;
+
+ case '*' : // wildcard
+ sb.append(rover.take());
+ break;
+
+ }
+ }
+ sb.append(rover.take('>'));
+ }
+ }
+
+ /**
+ * Handle the declaration part.
+ *
+ * @param sb
+ * @param map
+ * @param rover
+ */
+ private void declare(StringBuffer sb, Map<String, String> map, Rover rover) {
+ char c = rover.peek();
+ if (c == '<') {
+ sb.append(rover.take('<'));
+
+ while (rover.peek() != '>') {
+ String name = rover.upTo(":");
+ name = assign(map, name);
+ sb.append(name);
+ typeVar: while (rover.peek() == ':') {
+ sb.append(rover.take(':'));
+ switch (rover.peek()) {
+ case ':' : // empty class cases
+ continue typeVar;
+
+ default :
+ reference(sb, map, rover, false);
+ break;
+ }
+ }
+ }
+ sb.append(rover.take('>'));
+ }
+ }
+
+ /**
+ * Handles the assignment of type variables to index names so that
+ * we have a normalized name for each type var.
+ *
+ * @param map the map with variables.
+ * @param name The name of the variable
+ * @return the index name, like _1
+ */
+ private String assign(Map<String, String> map, String name) {
+ if (map.containsKey(name))
+ return map.get(name);
+ else {
+ int n = map.size();
+ map.put(name, "_" + n);
+ return "_" + n;
+ }
+ }
+
+ private boolean isInstance(Class<?> type, String string) {
+ if ( type == null)
+ return false;
+
+ if ( type.getName().equals(string))
+ return true;
+
+ if ( isInstance( type.getSuperclass(), string))
+ return true;
+
+ for ( Class<?> intf : type.getInterfaces()) {
+ if ( isInstance(intf,string))
+ return true;
+ }
+ return false;
+ }
+
+ private Object call(Object gd, String string) throws Exception {
+ Method m = gd.getClass().getMethod(string);
+ return m.invoke(gd);
+ }
+
+}