package com.fasterxml.jackson.databind.type;
import java.util.*;
import com.fasterxml.jackson.databind.
JavaType;
import com.fasterxml.jackson.databind.
util.
ClassUtil;
/**
* Simple recursive-descent parser for parsing canonical {@link JavaType}
* representations and constructing type instances.
*
* @author tatu
*/
public class
TypeParser
implements java.io.
Serializable
{
private static final long
serialVersionUID = 1L;
protected final
TypeFactory _factory;
public
TypeParser(
TypeFactory f) {
_factory =
f;
}
public
JavaType parse(
String canonical)
throws
IllegalArgumentException
{
canonical =
canonical.
trim();
MyTokenizer tokens = new
MyTokenizer(
canonical);
JavaType type =
parseType(
tokens);
// must be end, now
if (
tokens.
hasMoreTokens()) {
throw
_problem(
tokens, "Unexpected tokens after complete type");
}
return
type;
}
protected
JavaType parseType(
MyTokenizer tokens)
throws
IllegalArgumentException
{
if (!
tokens.
hasMoreTokens()) {
throw
_problem(
tokens, "Unexpected end-of-string");
}
Class<?>
base =
findClass(
tokens.
nextToken(),
tokens);
// either end (ok, non generic type), or generics
if (
tokens.
hasMoreTokens()) {
String token =
tokens.
nextToken();
if ("<".
equals(
token)) {
return
_factory.
_fromParameterizedClass(
base,
parseTypes(
tokens));
}
// can be comma that separates types, or closing '>'
tokens.
pushBack(
token);
}
return
_factory.
_fromClass(
base, null);
}
protected
List<
JavaType> parseTypes(
MyTokenizer tokens)
throws
IllegalArgumentException
{
ArrayList<
JavaType>
types = new
ArrayList<
JavaType>();
while (
tokens.
hasMoreTokens()) {
types.
add(
parseType(
tokens));
if (!
tokens.
hasMoreTokens()) break;
String token =
tokens.
nextToken();
if (">".
equals(
token)) return
types;
if (!",".
equals(
token)) {
throw
_problem(
tokens, "Unexpected token '"+
token+"', expected ',' or '>')");
}
}
throw
_problem(
tokens, "Unexpected end-of-string");
}
protected
Class<?>
findClass(
String className,
MyTokenizer tokens)
{
try {
return
ClassUtil.
findClass(
className);
} catch (
Exception e) {
if (
e instanceof
RuntimeException) {
throw (
RuntimeException)
e;
}
throw
_problem(
tokens, "Can not locate class '"+
className+"', problem: "+
e.
getMessage());
}
}
protected
IllegalArgumentException _problem(
MyTokenizer tokens,
String msg)
{
return new
IllegalArgumentException("Failed to parse type '"+
tokens.
getAllInput()
+"' (remaining: '"+
tokens.
getRemainingInput()+"'): "+
msg);
}
final static class
MyTokenizer
extends
StringTokenizer
{
protected final
String _input;
protected int
_index;
protected
String _pushbackToken;
public
MyTokenizer(
String str) {
super(
str, "<,>", true);
_input =
str;
}
@
Override
public boolean
hasMoreTokens() {
return (
_pushbackToken != null) || super.hasMoreTokens();
}
@
Override
public
String nextToken() {
String token;
if (
_pushbackToken != null) {
token =
_pushbackToken;
_pushbackToken = null;
} else {
token = super.nextToken();
}
_index +=
token.
length();
return
token;
}
public void
pushBack(
String token) {
_pushbackToken =
token;
_index -=
token.
length();
}
public
String getAllInput() { return
_input; }
public
String getUsedInput() { return
_input.
substring(0,
_index); }
public
String getRemainingInput() { return
_input.
substring(
_index); }
}
}