All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.bnd.compatibility.Signatures Maven / Gradle / Ivy

Go to download

The bndlib project is a general library to be used with OSGi bundles. It contains lots of cool functionality that calculates dependencies, etc.

There is a newer version: 2.4.0
Show newest version
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: 70abede2d1c976d3e6209b6bc04a94a4ac558faa $
 */
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.
	 */
	static 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:
	 * 
	 * 
	 * 	  class        ::= declaration? reference reference*
	 * 
* * @param f * @return * @throws Exception */ public String getSignature(Class< ? > c) throws Exception { StringBuilder sb = new StringBuilder(); 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: * *
	 *    method ::= declaration? '(' reference* ')' reference
	 * 
* * @param c * @return * @throws Exception */ public String getSignature(Method m) throws Exception { StringBuilder sb = new StringBuilder(); 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: * *
	 *    constructor ::= declaration? '(' reference* ')V'
	 * 
* * @param c * @return * @throws Exception */ public String getSignature(Constructor< ? > c) throws Exception { StringBuilder sb = new StringBuilder(); 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: * *
	 *    constructor ::= reference
	 * 
* * @param c * @return * @throws Exception */ public String getSignature(Field f) throws Exception { StringBuilder sb = new StringBuilder(); 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: *
	 *    declarations ::= '<' declaration ( ',' declaration )* '>'
	 *    declaration  ::= identifier ':' declare
	 *    declare      ::= types | variable 
	 *    types        ::= ( 'L' class ';' )? ( ':' 'L' interface ';' )*
	 *    variable     ::= 'T' id ';'
	 * 
* * @param sb * @param gd * @throws Exception */ private void declaration(StringBuilder 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. *
	 *   reference     ::= array | class | primitive | variable
	 *   array         ::= '[' reference
	 *   class         ::=  'L' body ( '.' body )* ';'
	 *   body          ::=  id ( '<' ( wildcard | reference )* '>' )?
	 *   variable      ::=  'T' id ';'
	 *   primitive     ::= PRIMITIVE
	 * 
* * @param sb * @param t * @throws Exception */ private void reference(StringBuilder 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(StringBuilder 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) { StringBuilder sb = new StringBuilder(); Map map = new HashMap(); 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(StringBuilder sb, Map 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(StringBuilder sb, Map 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(StringBuilder sb, Map 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 map, String name) { if (map.containsKey(name)) return map.get(name); 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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy