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 /** 9 * Simple recursive-descent parser for parsing canonical {@link JavaType} 10 * representations and constructing type instances. 11 */ 12 public class TypeParser 13 implements java.io.Serializable 14 { 15 private static final long serialVersionUID = 1L; 16 17 protected final TypeFactory _factory; 18 TypeParser(TypeFactory f)19 public TypeParser(TypeFactory f) { 20 _factory = f; 21 } 22 23 /** 24 * @since 2.6.2 25 */ withFactory(TypeFactory f)26 public TypeParser withFactory(TypeFactory f) { 27 return (f == _factory) ? this : new TypeParser(f); 28 } 29 parse(String canonical)30 public JavaType parse(String canonical) throws IllegalArgumentException 31 { 32 MyTokenizer tokens = new MyTokenizer(canonical.trim()); 33 JavaType type = parseType(tokens); 34 // must be end, now 35 if (tokens.hasMoreTokens()) { 36 throw _problem(tokens, "Unexpected tokens after complete type"); 37 } 38 return type; 39 } 40 parseType(MyTokenizer tokens)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 // either end (ok, non generic type), or generics 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 // can be comma that separates types, or closing '>' 58 tokens.pushBack(token); 59 } 60 return _factory._fromClass(null, base, TypeBindings.emptyBindings()); 61 } 62 parseTypes(MyTokenizer tokens)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 findClass(String className, MyTokenizer tokens)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 _problem(MyTokenizer tokens, String msg)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 MyTokenizer(String str)103 public MyTokenizer(String str) { 104 super(str, "<,>", true); 105 _input = str; 106 } 107 108 @Override hasMoreTokens()109 public boolean hasMoreTokens() { 110 return (_pushbackToken != null) || super.hasMoreTokens(); 111 } 112 113 @Override nextToken()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 pushBack(String token)127 public void pushBack(String token) { 128 _pushbackToken = token; 129 // let's NOT change index for now, since token may have been trim()ed 130 } 131 getAllInput()132 public String getAllInput() { return _input; } 133 // public String getUsedInput() { return _input.substring(0, _index); } getRemainingInput()134 public String getRemainingInput() { return _input.substring(_index); } 135 } 136 } 137