/* Copyright (c) 2008-2014, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. There is NO WARRANTY for this software. See license.txt for details. */ package java.lang.reflect; import avian.Classes; import java.util.ArrayList; import java.util.List; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; public class SignatureParser { private final ClassLoader loader; private final char[] array; private int offset; private final Type type; private final Map typeVariables; public static Type parse(ClassLoader loader, String signature, Class declaringClass) { return new SignatureParser(loader, signature, collectTypeVariables(declaringClass)).type; } private static Type parse(ClassLoader loader, String signature, Map typeVariables) { return new SignatureParser(loader, signature, typeVariables).type; } private SignatureParser(ClassLoader loader, String signature, Map typeVariables) { this.loader = loader; array = signature.toCharArray(); this.typeVariables = typeVariables; type = parseType(); if (offset != array.length) { throw new IllegalArgumentException("Extra characters after " + offset + ": " + signature); } } private Type parseType() { char c = array[offset++]; if (c == 'B') { return Byte.TYPE; } else if (c == 'C') { return Character.TYPE; } else if (c == 'D') { return Double.TYPE; } else if (c == 'F') { return Float.TYPE; } else if (c == 'I') { return Integer.TYPE; } else if (c == 'J') { return Long.TYPE; } else if (c == 'S') { return Short.TYPE; } else if (c == 'Z') { return Boolean.TYPE; } else if (c == 'T') { StringBuilder tnsb = new StringBuilder(); while ((c = array[offset++]) != ';') { tnsb.append(c); } return typeVariables.get(tnsb.toString()); } else if (c != 'L') { throw new IllegalArgumentException("Unexpected character: " + c); } StringBuilder builder = new StringBuilder(); Type ownerType = null; for (;;) { for (;;) { c = array[offset++]; if (c == ';' || c == '<') { break; } builder.append(c == '/' ? '.' : c); } String rawTypeName = builder.toString(); Class rawType; try { rawType = loader.loadClass(rawTypeName); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not find class " + rawTypeName); } int lastDollar = rawTypeName.lastIndexOf('$'); if (lastDollar != -1 && ownerType == null) { String ownerName = rawTypeName.substring(0, lastDollar); try { ownerType = loader.loadClass(ownerName); } catch (ClassNotFoundException e) { throw new RuntimeException("Could not find class " + ownerName); } } if (c == ';') { return rawType; } List args = new ArrayList(); while (array[offset] != '>') { args.add(parseType()); } ++offset; c = array[offset++]; ParameterizedType type = makeType(args.toArray(new Type[args.size()]), ownerType, rawType); if (c == ';') { return type; } if (c != '.') { throw new RuntimeException("TODO"); } ownerType = type; builder.append("$"); } } private static String typeName(Type type) { if (type instanceof Class) { Class clazz = (Class) type; return clazz.getName(); } return type.toString(); } private static ParameterizedType makeType(final Type[] args, final Type owner, final Type raw) { return new ParameterizedType() { @Override public Type getRawType() { return raw; } @Override public Type getOwnerType() { return owner; } @Override public Type[] getActualTypeArguments() { return args; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(typeName(raw)); builder.append('<'); String sep = ""; for (Type t : args) { builder.append(sep).append(typeName(t)); sep = ", "; } builder.append('>'); return builder.toString(); } }; } private static Map collectTypeVariables(Class clz) { Map varsMap = new HashMap(); LinkedList classList = new LinkedList(); for (Class c = clz; c != null; c = c.getDeclaringClass()) { classList.addFirst(c); } for (Class cur : classList) { final List varsList = new LinkedList(); if (cur.vmClass.addendum != null && cur.vmClass.addendum.signature != null) { String signature = Classes.toString((byte[]) cur.vmClass.addendum.signature); final char[] signChars = signature.toCharArray(); int i = 0; if (signChars[i] == '<') { i++; do { StringBuilder typeVarSB = new StringBuilder(); while (signChars[i] != ':') { typeVarSB.append(signChars[i]); i++; } String typeVarName = typeVarSB.toString(); i++; StringBuilder typeSB = new StringBuilder(); int angles = 0; while (angles > 0 || signChars[i] != ';') { if (signChars[i] == '<') angles ++; else if (signChars[i] == '>') angles --; typeSB.append(signChars[i]); i++; } typeSB.append(signChars[i]); String typeName = typeSB.toString(); final Type baseType = SignatureParser.parse(cur.vmClass.loader, typeName, varsMap); TypeVariable tv = new TypeVariableImpl(typeVarName, baseType); varsList.add(tv); i++; } while (signChars[i] != '>'); } } for (TypeVariable tv : varsList) { ((TypeVariableImpl)tv).setVars(varsList); varsMap.put(tv.getName(), tv); } cur = cur.getDeclaringClass(); }; return varsMap; } private static class TypeVariableImpl implements TypeVariable { private String name; private Type baseType; private TypeVariable[] vars; public Type[] getBounds() { return new Type[] { baseType }; } public GenericDeclaration getGenericDeclaration() { return new GenericDeclaration() { public TypeVariable[] getTypeParameters() { return vars; } }; } public String getName() { return name; } TypeVariableImpl(String name, Type baseType) { this.name = name; this.baseType = baseType; } void setVars(List vars) { this.vars = new TypeVariable[vars.size()]; vars.toArray(this.vars); } @Override public String toString() { return name; } } }