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);
+	}
+
+}