/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.wink.common.internal.type;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
/**
* Simple recursive-descent parser for parsing canonical {@link JavaType}
* representations and constructing type instances.
*
* @author tatu
* @since 1.5
*/
public class TypeParser {
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 {
/* [JACKSON-350]: Default Class.forName() won't work too well; context class loader
* seems like slightly better choice
*/
// return Class.forName(className);
ClassLoader loader = Thread.currentThread().getContextClassLoader();
return Class.forName(className, true, loader);
} 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);
}
}
}