org.jboss.jandex.GenericSignatureParser Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.jboss.jandex;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A simple recursive decent generic signature parser.
*
* @author Jason T. Greene
*/
class GenericSignatureParser {
/*
* Complete Grammar (VM Spec v8)
*
* JavaTypeSignature:
* ReferenceTypeSignature
* BaseType
*
* BaseType:
* B C D F I J S Z
*
* VoidDescriptor:
* V
*
* ReferenceTypeSignature:
* ClassTypeSignature
* TypeVariableSignature
* ArrayTypeSignature
*
* ClassTypeSignature:
* L [PackageSpecifier] SimpleClassTypeSignature {ClassTypeSignatureSuffix} ;
*
* PackageSpecifier:
* Identifier / {PackageSpecifier}
*
* SimpleClassTypeSignature:
* Identifier [TypeArguments]
*
* TypeArguments:
* < TypeArgument {TypeArgument} >
*
* TypeArgument:
* [WildcardIndicator] ReferenceTypeSignature
* *
*
* WildcardIndicator:
* +
* -
*
* ClassTypeSignatureSuffix:
* . SimpleClassTypeSignature
*
* TypeVariableSignature:
* T Identifier ;
*
* ArrayTypeSignature:
* [ JavaTypeSignature
*
* ClassSignature:
* [TypeParameters] SuperclassSignature {SuperinterfaceSignature}
*
* TypeParameters:
* < TypeParameter {TypeParameter} >
*
* TypeParameter:
* Identifier ClassBound {InterfaceBound}
*
* ClassBound:
* : [ReferenceTypeSignature]
*
* InterfaceBound:
* : ReferenceTypeSignature
*
* SuperclassSignature:
* ClassTypeSignature
*
* SuperinterfaceSignature:
* ClassTypeSignature
*
* MethodSignature:
* [TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}
*
* Result:
* JavaTypeSignature
* VoidDescriptor
*
* ThrowsSignature:
* ^ ClassTypeSignature
* ^ TypeVariableSignature
*
* FieldSignature:
* ReferenceTypeSignature
*
*/
private static WildcardType UNBOUNDED_WILDCARD = new WildcardType(null, true);
private String signature;
private int pos;
private NameTable names;
private Map typeParameters;
private Map elementTypeParameters = new HashMap();
private Map classTypeParameters = new HashMap();
GenericSignatureParser(NameTable names) {
names.intern(DotName.OBJECT_NAME, '/');
this.names = names;
}
static class ClassSignature {
private final Type[] parameters;
private final Type superClass;
private final Type[] interfaces;
private ClassSignature(Type[] parameters, Type superClass, Type[] interfaces) {
this.parameters = parameters;
this.superClass = superClass;
this.interfaces = interfaces;
}
Type[] parameters() {
return parameters;
}
Type superClass() {
return superClass;
}
Type[] interfaces() {
return interfaces;
}
public String toString() {
StringBuilder builder = new StringBuilder();
if (parameters.length > 0) {
builder.append('<');
builder.append(parameters[0]);
for (int i = 1; i < parameters.length; i++) {
builder.append(", ").append(parameters[i]);
}
builder.append('>');
}
if (superClass.name() != DotName.OBJECT_NAME) {
builder.append(" extends ").append(superClass);
}
if (interfaces.length > 0) {
builder.append(" implements ").append(interfaces[0]);
for (int i = 1; i < interfaces.length; i++) {
builder.append(", ").append(interfaces[i]);
}
}
return builder.toString();
}
}
static class MethodSignature {
private final Type[] typeParameters;
private final Type[] methodParameters;
private final Type returnType;
private final Type[] throwables;
private MethodSignature(Type[] typeParameters, Type[] methodParameters, Type returnType, Type[] throwables) {
this.typeParameters = typeParameters;
this.methodParameters = methodParameters;
this.returnType = returnType;
this.throwables = throwables;
}
public Type[] typeParameters() {
return typeParameters;
}
public Type returnType() {
return returnType;
}
public Type[] methodParameters() {
return methodParameters;
}
public Type[] throwables() {
return throwables;
}
public String toString() {
StringBuilder builder = new StringBuilder();
if (typeParameters.length > 0) {
builder.append("<");
builder.append(typeParameters[0]);
for (int i = 1; i < typeParameters.length; i++) {
builder.append(", ").append(typeParameters[i]);
}
builder.append("> ");
}
builder.append(returnType).append(" (");
if (methodParameters.length > 0) {
builder.append(methodParameters[0]);
for (int i = 1; i < methodParameters.length; i++) {
builder.append(", ").append(methodParameters[i]);
}
}
builder.append(')');
if (throwables.length > 0) {
builder.append(" throws ").append(throwables[0]);
for (int i = 1; i < throwables.length; i++) {
builder.append(", ").append(throwables[i]);
}
}
return builder.toString();
}
}
ClassSignature parseClassSignature(String signature) {
this.signature = signature;
this.typeParameters = this.classTypeParameters;
this.typeParameters.clear();
this.pos = 0;
Type[] parameters = parseTypeParameters();
Type superClass = names.intern(parseClassTypeSignature());
int end = signature.length();
List interfaces = new ArrayList();
while (pos < end) {
interfaces.add(names.intern(parseClassTypeSignature()));
}
Type[] intfArray = names.intern(interfaces.toArray(new Type[interfaces.size()]));
return new ClassSignature(parameters, superClass, intfArray);
}
private void expect(char c) {
if (signature.charAt(pos++) != c) {
throw new IllegalArgumentException("Expected character '" + c + "' at position " + (pos - 1));
}
}
Type parseFieldSignature(String signature) {
this.signature = signature;
this.typeParameters = this.elementTypeParameters;
this.typeParameters.clear();
this.pos = 0;
return parseReferenceType();
}
MethodSignature parseMethodSignature(String signature) {
this.signature = signature;
this.typeParameters = this.elementTypeParameters;
this.typeParameters.clear();
this.pos = 0;
Type[] typeParameters = parseTypeParameters();
expect('(');
List parameters = new ArrayList();
while (signature.charAt(pos) != ')') {
Type type = parseJavaType();
if (type == null) {
throw new IllegalArgumentException("Corrupted argument, or unclosed brace at: " + pos);
}
parameters.add(type);
}
pos++;
Type returnType = parseReturnType();
List exceptions = new ArrayList();
while (pos < signature.length()) {
expect('^');
exceptions.add(parseReferenceType());
}
Type[] exceptionsArray = names.intern(exceptions.toArray(new Type[exceptions.size()]));
Type[] types = names.intern(parameters.toArray(new Type[parameters.size()]));
return new MethodSignature(typeParameters, types, returnType, exceptionsArray);
}
private Type parseClassTypeSignature() {
String signature = this.signature;
DotName name = parseName();
Type[] types = parseTypeArguments();
Type type = null;
if (types.length > 0) {
type = new ParameterizedType(name, types, null);
}
// Suffix
while (signature.charAt(pos) == '.') {
int mark = ++pos;
int suffixEnd = advanceNameEnd();
name = names.wrap(name, signature.substring(mark, suffixEnd), true);
types = parseTypeArguments();
// A suffix is a parameterized type if it has typeParameters or it's owner is a parameterized type
// The first parameterized type needs a standard class type for the owner
if (type == null && types.length > 0) {
type = names.intern(new ClassType(name.prefix()));
}
if (type != null) {
type = names.intern(new ParameterizedType(name, types, type));
}
}
this.pos++; // ;
return type != null ? type : names.intern(new ClassType(name));
}
private Type[] parseTypeArguments() {
return parseTypeList(true);
}
private Type[] parseTypeParameters() {
return parseTypeList(false);
}
private Type[] parseTypeList(boolean argument) {
String signature = this.signature;
if (signature.charAt(pos) != '<') {
return Type.EMPTY_ARRAY;
}
pos++;
ArrayList types = new ArrayList();
for (;;) {
Type t = argument ? parseTypeArgument() : parseTypeParameter();
if (t == null) {
break;
}
types.add(t);
}
if (!argument) {
resolveTypeList(types);
}
return names.intern(types.toArray(new Type[types.size()]));
}
private Type parseTypeArgument() {
char c = signature.charAt(pos++);
switch (c) {
case '>':
return null;
case '*':
return UNBOUNDED_WILDCARD;
case '-': {
return parseWildCard(false);
}
case '+': {
return parseWildCard(true);
}
default:
pos--;
return parseReferenceType();
}
}
private Type parseWildCard(boolean isExtends) {
Type bound = parseReferenceType();
return new WildcardType(bound, isExtends);
}
private Type parseTypeParameter() {
int start = pos;
String signature = this.signature;
if (signature.charAt(start) == '>') {
pos++;
return null;
}
int bound = advancePast(':');
String name = names.intern(signature.substring(start, bound));
ArrayList bounds = new ArrayList();
// Class bound has an optional reference type
if (signature.charAt(pos) != ':') {
bounds.add(parseReferenceType());
}
// Interface bounds are none to many, with a required reference type
while (signature.charAt(pos) == ':') {
pos++;
bounds.add(parseReferenceType());
}
TypeVariable type = new TypeVariable(name, bounds.toArray(new Type[bounds.size()]));
typeParameters.put(name, type);
return type;
}
private Type parseReturnType() {
if (signature.charAt(pos) == 'V') {
pos++;
return VoidType.VOID;
}
return parseJavaType();
}
private Type parseReferenceType() {
int mark = pos;
char c = signature.charAt(mark);
Type type;
switch (c) {
case 'T':
type = parseTypeVariable();
break;
case 'L':
type = parseClassTypeSignature();
break;
case '[':
type = parseArrayType();
break;
default:
return null;
}
return names.intern(type);
}
private Type parseArrayType() {
int mark = this.pos;
int last = advanceNot('[');
return new ArrayType(parseJavaType(), last - mark);
}
private Type parseTypeVariable() {
String name = names.intern(signature.substring(pos + 1, advancePast(';')));
Type type = resolveType(name);
return type == null ? new UnresolvedTypeVariable(name) : type;
}
private void resolveTypeList(ArrayList list) {
int size = list.size();
for (int i = 0; i < size; i++) {
Type type = resolveType(list.get(i));
if (type != null) {
list.set(i, type);
typeParameters.put(type.asTypeVariable().identifier(), type.asTypeVariable());
}
}
}
private Type resolveType(Type type) {
if (type instanceof TypeVariable) {
TypeVariable typeVariable = resolveBounds(type);
return typeVariable != type ? typeVariable : null;
}
if (! (type instanceof UnresolvedTypeVariable)) {
return null;
}
return resolveType(((UnresolvedTypeVariable) type).identifier());
}
private TypeVariable resolveBounds(Type type) {
TypeVariable typeVariable = type.asTypeVariable();
Type[] bounds = typeVariable.boundArray();
for (int i = 0; i < bounds.length; i++) {
Type newType = resolveType(bounds[i]);
if (newType != null && newType != bounds[i]) {
typeVariable = typeVariable.copyType(i, newType);
}
}
return typeVariable;
}
private TypeVariable resolveType(String identifier) {
TypeVariable ret = elementTypeParameters.get(identifier);
return ret == null ? classTypeParameters.get(identifier) : ret;
}
private Type parseJavaType() {
Type type = PrimitiveType.decode(signature.charAt(pos));
if (type != null) {
pos++;
return type;
}
return parseReferenceType();
}
private int advancePast(char c) {
int pos = signature.indexOf(c, this.pos);
if (pos == -1) {
throw new IllegalStateException("Corruption");
}
this.pos = pos + 1;
return pos;
}
private int advanceNot(char c) {
while (signature.charAt(pos) == c) {
pos++;
}
return pos;
}
private DotName parseName() {
int start = pos;
int end = advanceNameEnd();
if (signature.charAt(start++) != 'L') {
throw new IllegalArgumentException("Invalid signature, invalid class designator");
}
return names.convertToName(signature.substring(start, end), '/');
}
private int advanceNameEnd() {
int end = pos;
String signature = this.signature;
for (; end < signature.length(); end++) {
char c = signature.charAt(end);
if (c == '.' || c == '<' || c ==';') {
return pos = end;
}
}
throw new IllegalStateException("Corrupted name");
}
public static void main(String[] args) throws IOException {
GenericSignatureParser parser = new GenericSignatureParser(new NameTable());
MethodSignature sig1 = parser.parseMethodSignature("(Ljava/lang/Class;TU;)Ljava/lang/Class<+TU;>;");
// MethodSignature sig1 = parser.parseMethodSignature("(Ljava/lang/Class;TU;)Ljava/lang/Class<+TU;>;");
// MethodSignature sig2 = parser.parseMethodSignature("(Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map;");
// MethodSignature sig3 = parser.parseMethodSignature("(Ljava/util/Collection<-TT;>;[TT;)Z");
// MethodSignature sig4 = parser.parseMethodSignature("(Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z");
// MethodSignature sig7 = parser.parseMethodSignature("()Lcom/sun/xml/internal/bind/v2/model/impl/ElementInfoImpl.PropertyImpl;");
// ClassSignature sig5 = parser.parseClassSignature(";R:Lio/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel;S:Lio/undertow/server/protocol/framed/AbstractFramedStreamSinkChannel;>Ljava/lang/Object;Lorg/xnio/channels/ConnectedChannel;");
// ClassSignature sig6 = parser.parseClassSignature("Lcom/apple/laf/AquaUtils$RecyclableSingleton;");
// System.out.println(sig1);
// System.out.println(sig2);
// System.out.println(sig3);
// System.out.println(sig4);
// System.out.println(sig5);
// System.out.println(sig6);
// System.out.println(sig7);
// BufferedReader reader = new BufferedReader(new FileReader("/Users/jason/sigmethods.txt"));
// String line;
// while ((line = reader.readLine()) != null) {
// try {
// System.out.println(parser.parseMethodSignature(line));
// } catch (Exception e) {
// System.err.println(line);
// e.printStackTrace(System.err);
// System.exit(-1);
// }
// }
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy