1 package com.fasterxml.jackson.databind.type;
2
3 import java.util.*;
4
5 import com.fasterxml.jackson.databind.JavaType;
6 import com.fasterxml.jackson.databind.util.ClassUtil;
7
8
12 public class TypeParser
13 implements java.io.Serializable
14 {
15 private static final long serialVersionUID = 1L;
16
17 protected final TypeFactory _factory;
18
19 public TypeParser(TypeFactory f) {
20 _factory = f;
21 }
22
23
26 public TypeParser withFactory(TypeFactory f) {
27 return (f == _factory) ? this : new TypeParser(f);
28 }
29
30 public JavaType parse(String canonical) throws IllegalArgumentException
31 {
32 MyTokenizer tokens = new MyTokenizer(canonical.trim());
33 JavaType type = parseType(tokens);
34
35 if (tokens.hasMoreTokens()) {
36 throw _problem(tokens, "Unexpected tokens after complete type");
37 }
38 return type;
39 }
40
41 protected JavaType parseType(MyTokenizer tokens)
42 throws IllegalArgumentException
43 {
44 if (!tokens.hasMoreTokens()) {
45 throw _problem(tokens, "Unexpected end-of-string");
46 }
47 Class<?> base = findClass(tokens.nextToken(), tokens);
48
49
50 if (tokens.hasMoreTokens()) {
51 String token = tokens.nextToken();
52 if ("<".equals(token)) {
53 List<JavaType> parameterTypes = parseTypes(tokens);
54 TypeBindings b = TypeBindings.create(base, parameterTypes);
55 return _factory._fromClass(null, base, b);
56 }
57
58 tokens.pushBack(token);
59 }
60 return _factory._fromClass(null, base, TypeBindings.emptyBindings());
61 }
62
63 protected List<JavaType> parseTypes(MyTokenizer tokens)
64 throws IllegalArgumentException
65 {
66 ArrayList<JavaType> types = new ArrayList<JavaType>();
67 while (tokens.hasMoreTokens()) {
68 types.add(parseType(tokens));
69 if (!tokens.hasMoreTokens()) break;
70 String token = tokens.nextToken();
71 if (">".equals(token)) return types;
72 if (!",".equals(token)) {
73 throw _problem(tokens, "Unexpected token '"+token+"', expected ',' or '>')");
74 }
75 }
76 throw _problem(tokens, "Unexpected end-of-string");
77 }
78
79 protected Class<?> findClass(String className, MyTokenizer tokens)
80 {
81 try {
82 return _factory.findClass(className);
83 } catch (Exception e) {
84 ClassUtil.throwIfRTE(e);
85 throw _problem(tokens, "Cannot locate class '"+className+"', problem: "+e.getMessage());
86 }
87 }
88
89 protected IllegalArgumentException _problem(MyTokenizer tokens, String msg)
90 {
91 return new IllegalArgumentException(String.format("Failed to parse type '%s' (remaining: '%s'): %s",
92 tokens.getAllInput(), tokens.getRemainingInput(), msg));
93 }
94
95 final static class MyTokenizer extends StringTokenizer
96 {
97 protected final String _input;
98
99 protected int _index;
100
101 protected String _pushbackToken;
102
103 public MyTokenizer(String str) {
104 super(str, "<,>", true);
105 _input = str;
106 }
107
108 @Override
109 public boolean hasMoreTokens() {
110 return (_pushbackToken != null) || super.hasMoreTokens();
111 }
112
113 @Override
114 public String nextToken() {
115 String token;
116 if (_pushbackToken != null) {
117 token = _pushbackToken;
118 _pushbackToken = null;
119 } else {
120 token = super.nextToken();
121 _index += token.length();
122 token = token.trim();
123 }
124 return token;
125 }
126
127 public void pushBack(String token) {
128 _pushbackToken = token;
129
130 }
131
132 public String getAllInput() { return _input; }
133
134 public String getRemainingInput() { return _input.substring(_index); }
135 }
136 }
137