xapi.reflect.impl.SignatureReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xapi-dev Show documentation
Show all versions of xapi-dev Show documentation
Everything needed to run a comprehensive dev environment.
Just type X_ and pick a service from autocomplete;
new dev modules will be added as they are built.
The only dev service not included in the uber jar is xapi-dev-maven,
as it includes all runtime dependencies of maven, adding ~4 seconds to build time,
and 6 megabytes to the final output jar size (without xapi-dev-maven, it's ~1MB).
The newest version!
/***
* Modified by We The Internet to support arrays of inner classes.
*
*
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2007 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package xapi.reflect.impl;
import xapi.reflect.api.SignatureVisitor;
/**
* A type signature parser to make a signature visitor visit an existing
* signature.
*
* @author Thomas Hallgren
* @author Eric Bruneton
*/
public class SignatureReader {
/**
* The signature to be read.
*/
private final String signature;
/**
* Constructs a {@link SignatureReader} for the given signature.
*
* @param signature A ClassSignature, MethodTypeSignature,
* or FieldTypeSignature.
*/
public SignatureReader(final String signature) {
this.signature = signature;
}
/**
* Makes the given visitor visit the signature of this
* {@link SignatureReader}. This signature is the one specified in the
* constructor (see {@link #SignatureReader(String) SignatureReader}). This
* method is intended to be called on a {@link SignatureReader} that was
* created using a ClassSignature (such as the
* signature
parameter of the
* {@link com.google.gwt.dev.asm.ClassVisitor#visit ClassVisitor.visit} method)
* or a MethodTypeSignature (such as the signature
* parameter of the
* {@link com.google.gwt.dev.asm.ClassVisitor#visitMethod ClassVisitor.visitMethod}
* method).
*
* @param v the visitor that must visit this signature.
*/
public void accept(final SignatureVisitor v) {
String signature = this.signature;
int len = signature.length();
int pos;
char c;
if (signature.charAt(0) == '<') {
pos = 2;
do {
int end = signature.indexOf(':', pos);
v.visitFormalTypeParameter(signature.substring(pos - 1, end));
pos = end + 1;
c = signature.charAt(pos);
if (c == 'L' || c == '[' || c == 'T') {
pos = parseType(signature, pos, v.visitClassBound());
}
while ((c = signature.charAt(pos++)) == ':') {
pos = parseType(signature, pos, v.visitInterfaceBound());
}
} while (c != '>');
} else {
pos = 0;
}
if (signature.charAt(pos) == '(') {
pos++;
while (signature.charAt(pos) != ')') {
pos = parseType(signature, pos, v.visitParameterType());
}
pos = parseType(signature, pos + 1, v.visitReturnType());
while (pos < len) {
pos = parseType(signature, pos + 1, v.visitExceptionType());
}
} else {
pos = parseType(signature, pos, v.visitSuperclass());
while (pos < len) {
pos = parseType(signature, pos, v.visitInterface());
}
}
}
/**
* Makes the given visitor visit the signature of this
* {@link SignatureReader}. This signature is the one specified in the
* constructor (see {@link #SignatureReader(String) SignatureReader}). This
* method is intended to be called on a {@link SignatureReader} that was
* created using a FieldTypeSignature, such as the
* signature
parameter of the
* {@link com.google.gwt.dev.asm.ClassVisitor#visitField
* ClassVisitor.visitField} or {@link
* com.google.gwt.dev.asm.MethodVisitor#visitLocalVariable
* MethodVisitor.visitLocalVariable} methods.
*
* @param v the visitor that must visit this signature.
*/
public void acceptType(final SignatureVisitor v) {
parseType(this.signature, 0, v);
}
/**
* Parses a field type signature and makes the given visitor visit it.
*
* @param signature a string containing the signature that must be parsed.
* @param pos index of the first character of the signature to parsed.
* @param v the visitor that must visit this signature.
* @return the index of the first character after the parsed signature.
*/
private static int parseType(
final String signature,
int pos,
final SignatureVisitor v)
{
char c;
int start, end;
boolean visited, inner;
String name;
switch (c = signature.charAt(pos++)) {
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
case 'F':
case 'J':
case 'D':
case 'V':
v.visitBaseType(c);
return pos;
case '[':
// This might be an inner class array!
return parseArrayType(signature, pos, 1, v);
case 'T':
end = signature.indexOf(';', pos);
v.visitTypeVariable(signature.substring(pos, end));
return end + 1;
default: // case 'L':
start = pos;
visited = false;
inner = false;
for (;;) {
switch (c = signature.charAt(pos++)) {
case '.':
case ';':
if (!visited) {
name = signature.substring(start, pos - 1);
if (inner) {
v.visitInnerClassType(name);
} else {
v.visitClassType(name);
}
}
if (c == ';') {
v.visitEnd();
return pos;
}
start = pos;
visited = false;
inner = true;
break;
case '<':
name = signature.substring(start, pos - 1);
if (inner) {
v.visitInnerClassType(name);
} else {
v.visitClassType(name);
}
visited = true;
top: for (;;) {
switch (c = signature.charAt(pos)) {
case '>':
break top;
case '*':
++pos;
v.visitTypeArgument();
break;
case '+':
case '-':
pos = parseType(signature,
pos + 1,
v.visitTypeArgument(c));
break;
default:
pos = parseType(signature,
pos,
v.visitTypeArgument('='));
break;
}
}
}
}
}
}
/**
* Parses a field type signature and makes the given visitor visit it.
*
* This is called after we already know the type is an array.
* We must parse this case specially, for when we have arrays of inner classes.
*
* @param signature a string containing the signature that must be parsed.
* @param pos index of the first character of the signature to parsed.
* @param v the visitor that must visit this signature.
* @return the index of the first character after the parsed signature.
*/
private static int parseArrayType(
final String signature,
int pos, int depth,
SignatureVisitor v)
{
char c;
int start, end;
boolean visited, inner;
String name;
switch (c = signature.charAt(pos++)) {
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
case 'F':
case 'J':
case 'D':
case 'V':
while(depth-->0)
v = v.visitArrayType();
v.visitBaseType(c);
return pos;
case '[':
// Multi dimensional array
return parseArrayType(signature, pos, depth+1, v);
case 'T':
// we probably can't get to type, unless <[TT;> syntax is supported...
end = signature.indexOf(';', pos);
v.visitTypeVariable(signature.substring(pos, end));
return end + 1;
default: // case 'L':
start = pos;
visited = false;
inner = false;
for (;;) {
switch (c = signature.charAt(pos++)) {
case ';':
// we've hit the innermost class, consume array depth now.
while(depth-->0)
v = v.visitArrayType();
case '.':
if (!visited) {
name = signature.substring(start, pos - 1);
if (inner) {
v.visitInnerClassType(name);
} else {
v.visitClassType(name);
}
}
if (c == ';') {
v.visitEnd();
return pos;
}
start = pos;
visited = false;
inner = true;
break;
case '<':
// if we hit a generic while we have an array depth pending,
// we need to know if we are going to have an inner class or not.
// this is a little tricky, since generics are definitely involved.
// so, we search ahead, and keep count of < and > occurrences
int
nextOpen = signature.indexOf('<', pos)
,nextClose = signature.indexOf('>', pos)
,open = 1
;
name = signature.substring(start, pos - 1);
top: for(;;) {
if (nextOpen == -1) {
// no more open generics; make sure we have the correct end index
// find next inner class
int nextPeriod = signature.indexOf('.', nextClose);
// find next type closure. Note that we can't hit a generic
// closing ;, since we're starting after the current generic
// is closed, and a later generic type will only count if it
// is part of an inner class.
int nextSemiColon = signature.indexOf(';', nextClose);
if (nextPeriod == -1 || nextPeriod > nextSemiColon) {
// no inner class, safe for us to consume array depth.
while(depth-->0)
v = v.visitArrayType();
}
// there is an inner class, array depth is meant for it (or its child).
break top;
}
// check for the case of open generic after our greatest closing generic
while(nextOpen > nextClose) {
// pop off depth to see if this is the end
if (--open==0) {
//generic is closed, we can check for inner class now.
int nextPeriod = signature.indexOf('.', nextClose);
int nextSemiColon = signature.indexOf(';', nextClose);
if (nextPeriod == -1 || nextPeriod > nextSemiColon) {
//no more inner class, safe for us to consume array depth
while(depth-->0)
v = v.visitArrayType();
}
}
// keep looking for > closes
nextClose = signature.indexOf('>', nextClose+1);
assert nextClose > -1 : "Generics not closed in signature "+signature;
}
// while next open hasn't reached next close, put onto open stack
while (0 < nextOpen && nextOpen < nextClose) {
open++;
nextOpen = signature.indexOf('<', nextOpen+1);// might be -1
}
}// end top for
if (inner) {
v.visitInnerClassType(name);
} else {
v.visitClassType(name);
}
visited = true;
top: for (;;) {
switch (c = signature.charAt(pos)) {
case '>':
break top;
case '*':
++pos;
v.visitTypeArgument();
break;
case '+':
case '-':
pos = parseType(signature,
pos + 1,
v.visitTypeArgument(c));
break;
default:
pos = parseType(signature,
pos,
v.visitTypeArgument('='));
break;
}
}
}
}
}
}
}