| /* GenericSignatureParser.java | |
| Copyright (C) 2005 | |
| Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.lang.reflect; | |
| import gnu.java.lang.CPStringBuilder; | |
| import java.lang.reflect.Constructor; | |
| import java.lang.reflect.GenericArrayType; | |
| import java.lang.reflect.GenericDeclaration; | |
| import java.lang.reflect.GenericSignatureFormatError; | |
| import java.lang.reflect.MalformedParameterizedTypeException; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.ParameterizedType; | |
| import java.lang.reflect.Type; | |
| import java.lang.reflect.TypeVariable; | |
| import java.lang.reflect.WildcardType; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| final class TypeVariableImpl extends TypeImpl implements TypeVariable | |
| { | |
| private GenericDeclaration decl; | |
| private Type[] bounds; | |
| private String name; | |
| TypeVariableImpl(GenericDeclaration decl, Type[] bounds, String name) | |
| { | |
| this.decl = decl; | |
| this.bounds = bounds; | |
| this.name = name; | |
| } | |
| Type resolve() | |
| { | |
| return this; | |
| } | |
| public Type[] getBounds() | |
| { | |
| resolve(bounds); | |
| return (Type[]) bounds.clone(); | |
| } | |
| public GenericDeclaration getGenericDeclaration() | |
| { | |
| return decl; | |
| } | |
| public String getName() | |
| { | |
| return name; | |
| } | |
| public boolean equals(Object obj) | |
| { | |
| if (obj instanceof TypeVariableImpl) | |
| { | |
| TypeVariableImpl other = (TypeVariableImpl)obj; | |
| return decl.equals(other.decl) && name.equals(other.name); | |
| } | |
| return false; | |
| } | |
| public int hashCode() | |
| { | |
| return 0x5f4d5156 ^ decl.hashCode() ^ name.hashCode(); | |
| } | |
| public String toString() | |
| { | |
| return name; | |
| } | |
| } | |
| final class ParameterizedTypeImpl extends TypeImpl implements ParameterizedType | |
| { | |
| private String rawTypeName; | |
| private ClassLoader loader; | |
| private Class rawType; | |
| private Type owner; | |
| private Type[] typeArgs; | |
| ParameterizedTypeImpl(String rawTypeName, ClassLoader loader, Type owner, | |
| Type[] typeArgs) | |
| { | |
| this.rawTypeName = rawTypeName; | |
| this.loader = loader; | |
| this.owner = owner; | |
| this.typeArgs = typeArgs; | |
| } | |
| Type resolve() | |
| { | |
| if (rawType == null) | |
| { | |
| try | |
| { | |
| rawType = Class.forName(rawTypeName, false, loader); | |
| } | |
| catch (ClassNotFoundException x) | |
| { | |
| throw new TypeNotPresentException(rawTypeName, x); | |
| } | |
| } | |
| if (typeArgs == null) | |
| { | |
| if (owner == null) | |
| { | |
| return rawType; | |
| } | |
| typeArgs = new Type[0]; | |
| } | |
| resolve(typeArgs); | |
| owner = resolve(owner); | |
| return this; | |
| } | |
| public Type[] getActualTypeArguments() | |
| { | |
| return (Type[]) typeArgs.clone(); | |
| } | |
| public Type getRawType() | |
| { | |
| return rawType; | |
| } | |
| public Type getOwnerType() | |
| { | |
| return owner; | |
| } | |
| public boolean equals(Object obj) | |
| { | |
| if (obj instanceof ParameterizedTypeImpl) | |
| { | |
| ParameterizedTypeImpl other = (ParameterizedTypeImpl)obj; | |
| return rawType.equals(other.rawType) | |
| && ((owner == null && other.owner == null) | |
| || owner.equals(other.owner)) | |
| && Arrays.deepEquals(typeArgs, other.typeArgs); | |
| } | |
| return false; | |
| } | |
| public int hashCode() | |
| { | |
| int h = 0x58158970 ^ rawType.hashCode(); | |
| if (owner != null) | |
| { | |
| h ^= Integer.reverse(owner.hashCode()); | |
| } | |
| for (int i = 0; i < typeArgs.length; i++) | |
| { | |
| h ^= Integer.rotateLeft(typeArgs[i].hashCode(), i); | |
| } | |
| return h; | |
| } | |
| public String toString() | |
| { | |
| CPStringBuilder sb = new CPStringBuilder(); | |
| if (owner != null) | |
| { | |
| sb.append(owner); | |
| sb.append('.'); | |
| sb.append(rawType.getSimpleName()); | |
| } | |
| else | |
| { | |
| sb.append(rawTypeName); | |
| } | |
| if (typeArgs.length > 0) | |
| { | |
| sb.append('<'); | |
| for (int i = 0; i < typeArgs.length; i++) | |
| { | |
| if (i > 0) | |
| sb.append(", "); | |
| if (typeArgs[i] instanceof Class) | |
| { | |
| sb.append(((Class)typeArgs[i]).getName()); | |
| } | |
| else | |
| { | |
| sb.append(typeArgs[i]); | |
| } | |
| } | |
| sb.append('>'); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| final class GenericArrayTypeImpl extends TypeImpl implements GenericArrayType | |
| { | |
| private Type componentType; | |
| GenericArrayTypeImpl(Type componentType) | |
| { | |
| this.componentType = componentType; | |
| } | |
| Type resolve() | |
| { | |
| componentType = resolve(componentType); | |
| return this; | |
| } | |
| public Type getGenericComponentType() | |
| { | |
| return componentType; | |
| } | |
| public boolean equals(Object obj) | |
| { | |
| if (obj instanceof GenericArrayTypeImpl) | |
| { | |
| GenericArrayTypeImpl other = (GenericArrayTypeImpl)obj; | |
| return componentType.equals(other.componentType); | |
| } | |
| return false; | |
| } | |
| public int hashCode() | |
| { | |
| return 0x4be37a7f ^ componentType.hashCode(); | |
| } | |
| public String toString() | |
| { | |
| return componentType + "[]"; | |
| } | |
| } | |
| final class UnresolvedTypeVariable extends TypeImpl implements Type | |
| { | |
| private GenericDeclaration decl; | |
| private String name; | |
| UnresolvedTypeVariable(GenericDeclaration decl, String name) | |
| { | |
| this.decl = decl; | |
| this.name = name; | |
| } | |
| Type resolve() | |
| { | |
| GenericDeclaration d = decl; | |
| while (d != null) | |
| { | |
| for (TypeVariable t : d.getTypeParameters()) | |
| { | |
| if (t.getName().equals(name)) | |
| { | |
| return t; | |
| } | |
| } | |
| d = getParent(d); | |
| } | |
| throw new MalformedParameterizedTypeException(); | |
| } | |
| private static GenericDeclaration getParent(GenericDeclaration d) | |
| { | |
| if (d instanceof Class) | |
| { | |
| Method m = ((Class)d).getEnclosingMethod(); | |
| if (m != null) | |
| { | |
| return m; | |
| } | |
| Constructor c = ((Class)d).getEnclosingConstructor(); | |
| if (c != null) | |
| { | |
| return c; | |
| } | |
| return ((Class)d).getEnclosingClass(); | |
| } | |
| else if (d instanceof Method) | |
| { | |
| return ((Method)d).getDeclaringClass(); | |
| } | |
| else if (d instanceof Constructor) | |
| { | |
| return ((Constructor)d).getDeclaringClass(); | |
| } | |
| else | |
| { | |
| // TODO figure out what this represents | |
| throw new Error(); | |
| } | |
| } | |
| } | |
| final class WildcardTypeImpl extends TypeImpl implements WildcardType | |
| { | |
| private Type lower; | |
| private Type upper; | |
| WildcardTypeImpl(Type lower, Type upper) | |
| { | |
| this.lower = lower; | |
| this.upper = upper; | |
| } | |
| Type resolve() | |
| { | |
| upper = resolve(upper); | |
| lower = resolve(lower); | |
| return this; | |
| } | |
| public Type[] getUpperBounds() | |
| { | |
| if (upper == null) | |
| { | |
| return new Type[0]; | |
| } | |
| return new Type[] { upper }; | |
| } | |
| public Type[] getLowerBounds() | |
| { | |
| if (lower == null) | |
| { | |
| return new Type[0]; | |
| } | |
| return new Type[] { lower }; | |
| } | |
| public boolean equals(Object obj) | |
| { | |
| if (obj instanceof WildcardTypeImpl) | |
| { | |
| WildcardTypeImpl other = (WildcardTypeImpl)obj; | |
| return Arrays.deepEquals(getUpperBounds(), other.getUpperBounds()) | |
| && Arrays.deepEquals(getLowerBounds(), other.getLowerBounds()); | |
| } | |
| return false; | |
| } | |
| public int hashCode() | |
| { | |
| int h = 0x75d074fd; | |
| if (upper != null) | |
| { | |
| h ^= upper.hashCode(); | |
| } | |
| if (lower != null) | |
| { | |
| h ^= lower.hashCode(); | |
| } | |
| return h; | |
| } | |
| public String toString() | |
| { | |
| if (lower != null) | |
| { | |
| return "? super " + lower; | |
| } | |
| if (upper == java.lang.Object.class) | |
| { | |
| return "?"; | |
| } | |
| return "? extends " + upper; | |
| } | |
| } | |
| class GenericSignatureParser | |
| { | |
| private ClassLoader loader; | |
| private GenericDeclaration container; | |
| private String signature; | |
| private int pos; | |
| GenericSignatureParser(GenericDeclaration container, ClassLoader loader, | |
| String signature) | |
| { | |
| this.container = container; | |
| this.loader = loader; | |
| this.signature = signature; | |
| } | |
| TypeVariable[] readFormalTypeParameters() | |
| { | |
| consume('<'); | |
| ArrayList<TypeVariable> params = new ArrayList<TypeVariable>(); | |
| do | |
| { | |
| // TODO should we handle name clashes? | |
| params.add(readFormalTypeParameter()); | |
| } while (peekChar() != '>'); | |
| consume('>'); | |
| TypeVariable[] list = new TypeVariable[params.size()]; | |
| params.toArray(list); | |
| return list; | |
| } | |
| private TypeVariable readFormalTypeParameter() | |
| { | |
| String identifier = readIdentifier(); | |
| consume(':'); | |
| ArrayList<Type> bounds = new ArrayList<Type>(); | |
| if (peekChar() != ':') | |
| { | |
| bounds.add(readFieldTypeSignature()); | |
| } | |
| while (peekChar() == ':') | |
| { | |
| consume(':'); | |
| bounds.add(readFieldTypeSignature()); | |
| } | |
| Type[] b = new Type[bounds.size()]; | |
| bounds.toArray(b); | |
| return new TypeVariableImpl(container, b, identifier); | |
| } | |
| Type readFieldTypeSignature() | |
| { | |
| switch (peekChar()) | |
| { | |
| case 'L': | |
| return readClassTypeSignature(); | |
| case '[': | |
| return readArrayTypeSignature(); | |
| case 'T': | |
| return readTypeVariableSignature(); | |
| default: | |
| throw new GenericSignatureFormatError(); | |
| } | |
| } | |
| Type readClassTypeSignature() | |
| { | |
| consume('L'); | |
| String className = ""; | |
| for (;;) | |
| { | |
| String part = readIdentifier(); | |
| if (peekChar() != '/') | |
| { | |
| className += part; | |
| break; | |
| } | |
| consume('/'); | |
| className += part + "."; | |
| } | |
| Type[] typeArguments = null; | |
| if (peekChar() == '<') | |
| { | |
| typeArguments = readTypeArguments(); | |
| } | |
| Type type = new ParameterizedTypeImpl(className, loader, null, | |
| typeArguments); | |
| while (peekChar() == '.') | |
| { | |
| consume('.'); | |
| className += "$" + readIdentifier(); | |
| typeArguments = null; | |
| if (peekChar() == '<') | |
| { | |
| typeArguments = readTypeArguments(); | |
| } | |
| type = new ParameterizedTypeImpl(className, loader, type, | |
| typeArguments); | |
| } | |
| consume(';'); | |
| return type; | |
| } | |
| private Type[] readTypeArguments() | |
| { | |
| consume('<'); | |
| ArrayList<Type> list = new ArrayList<Type>(); | |
| do | |
| { | |
| list.add(readTypeArgument()); | |
| } while ((peekChar() != '>')); | |
| consume('>'); | |
| Type[] arr = new Type[list.size()]; | |
| list.toArray(arr); | |
| return arr; | |
| } | |
| private Type readTypeArgument() | |
| { | |
| char c = peekChar(); | |
| if (c == '+') | |
| { | |
| consume('+'); | |
| return new WildcardTypeImpl(null, readFieldTypeSignature()); | |
| } | |
| else if (c == '-') | |
| { | |
| consume('-'); | |
| return new WildcardTypeImpl(readFieldTypeSignature(), | |
| java.lang.Object.class); | |
| } | |
| else if (c == '*') | |
| { | |
| consume('*'); | |
| return new WildcardTypeImpl(null, java.lang.Object.class); | |
| } | |
| else | |
| { | |
| return readFieldTypeSignature(); | |
| } | |
| } | |
| Type readArrayTypeSignature() | |
| { | |
| consume('['); | |
| switch (peekChar()) | |
| { | |
| case 'L': | |
| case '[': | |
| case 'T': | |
| return new GenericArrayTypeImpl(readFieldTypeSignature()); | |
| case 'Z': | |
| consume('Z'); | |
| return boolean[].class; | |
| case 'B': | |
| consume('B'); | |
| return byte[].class; | |
| case 'S': | |
| consume('S'); | |
| return short[].class; | |
| case 'C': | |
| consume('C'); | |
| return char[].class; | |
| case 'I': | |
| consume('I'); | |
| return int[].class; | |
| case 'F': | |
| consume('F'); | |
| return float[].class; | |
| case 'J': | |
| consume('J'); | |
| return long[].class; | |
| case 'D': | |
| consume('D'); | |
| return double[].class; | |
| default: | |
| throw new GenericSignatureFormatError(); | |
| } | |
| } | |
| Type readTypeVariableSignature() | |
| { | |
| consume('T'); | |
| String identifier = readIdentifier(); | |
| consume(';'); | |
| return new UnresolvedTypeVariable(container, identifier); | |
| } | |
| private String readIdentifier() | |
| { | |
| int start = pos; | |
| char c; | |
| do | |
| { | |
| readChar(); | |
| c = peekChar(); | |
| } while (";:./<>-+*".indexOf(c) == -1); | |
| return signature.substring(start, pos); | |
| } | |
| final char peekChar() | |
| { | |
| if (pos == signature.length()) | |
| return '\u0000'; | |
| else | |
| return signature.charAt(pos); | |
| } | |
| final char readChar() | |
| { | |
| return signature.charAt(pos++); | |
| } | |
| final void consume(char c) | |
| { | |
| if (readChar() != c) | |
| throw new GenericSignatureFormatError(); | |
| } | |
| final void end() | |
| { | |
| if (pos != signature.length()) | |
| throw new GenericSignatureFormatError(); | |
| } | |
| } |