/* * 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); } } }