/* * 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.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /** * Helper class used for resolving type parameters for given class * * @since 1.5 */ public class TypeBindings { /** * Marker to use for (temporarily) unbound references. */ public final static JavaType UNBOUND = new SimpleType(Object.class); /** * Context type used for resolving all types, if specified. May be null, * in which case {@link #_contextClass} is used instead. */ protected final JavaType _contextType; /** * Specific class to use for resolving all types, for methods and fields * class and its superclasses and -interfaces contain. */ protected final Class<?> _contextClass; /** * Lazily-instantiated bindings of resolved type parameters */ protected Map<String, JavaType> _bindings; /** * Also: we may temporarily want to mark certain named types * as resolved (but without exact type); if so, we'll just store * names here. */ protected HashSet<String> _placeholders; public TypeBindings(Class<?> cc) { _contextClass = cc; _contextType = null; } public TypeBindings(JavaType type) { _contextType = type; _contextClass = type.getRawClass(); } public int getBindingCount() { if (_bindings == null) { _resolve(); } return _bindings.size(); } public JavaType findType(String name) { if (_bindings == null) { _resolve(); } JavaType t = _bindings.get(name); if (t == null) { if (_placeholders != null && _placeholders.contains(name)) { t = UNBOUND; } else { // Should we throw an exception or just return null? throw new IllegalArgumentException("Type variable '" + name + "' can not be resolved (with context of class " + _contextClass.getName() + ")"); //t = UNBOUND; } } return t; } /* /*******************************************************************8 /* Internal methods /*******************************************************************8 */ protected void _resolve() { _resolveBindings(_contextClass); // finally: may have root level type info too if (_contextType != null) { int count = _contextType.containedTypeCount(); if (count > 0) { if (_bindings == null) { _bindings = new HashMap<String, JavaType>(); } for (int i = 0; i < count; ++i) { String name = _contextType.containedTypeName(i); JavaType type = _contextType.containedType(i); _bindings.put(name, type); } } } // nothing bound? mark with empty map to prevent further calls if (_bindings == null) { _bindings = Collections.emptyMap(); } } public void _addPlaceholder(String name) { if (_placeholders == null) { _placeholders = new HashSet<String>(); } _placeholders.add(name); } protected void _resolveBindings(Type t) { if (t == null) return; Class<?> raw; if (t instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType)t; Type[] args = pt.getActualTypeArguments(); if (args != null && args.length > 0) { Class<?> rawType = (Class<?>)pt.getRawType(); TypeVariable<?>[] vars = rawType.getTypeParameters(); if (vars.length != args.length) { throw new IllegalArgumentException("Strange parametrized type (in class " + rawType.getName() + "): number of type arguments != number of type parameters (" + args.length + " vs " + vars.length + ")"); } for (int i = 0, len = args.length; i < len; ++i) { TypeVariable<?> var = vars[i]; String name = var.getName(); if (_bindings == null) { _bindings = new HashMap<String, JavaType>(); } else { /* 24-Mar-2010, tatu: Better ensure that we do not overwrite something * collected earlier (since we descend towards super-classes): */ if (_bindings.containsKey(name)) continue; } // first: add a placeholder to prevent infinite loops _addPlaceholder(name); // then resolve type _bindings.put(name, TypeFactory.instance._fromType(args[i], this)); } } raw = (Class<?>)pt.getRawType(); } else if (t instanceof Class<?>) { raw = (Class<?>)t; /* 24-Mar-2010, tatu: Can not have true generics definitions, but can * have lower bounds ("<T extends BeanBase>") in declaration itself */ TypeVariable<?>[] vars = raw.getTypeParameters(); if (vars != null && vars.length > 0) { for (TypeVariable<?> var : vars) { String name = var.getName(); Type varType = var.getBounds()[0]; if (varType != null) { if (_bindings == null) { _bindings = new HashMap<String, JavaType>(); } else { // and no overwriting... if (_bindings.containsKey(name)) continue; } _addPlaceholder(name); // to prevent infinite loops _bindings.put(name, TypeFactory.instance._fromType(varType, this)); } } } } else { // probably can't be any of these... so let's skip for now //if (type instanceof GenericArrayType) { //if (type instanceof TypeVariable<?>) { // if (type instanceof WildcardType) { return; } // but even if it's not a parameterized type, its super types may be: _resolveBindings(raw.getGenericSuperclass()); for (Type intType : raw.getGenericInterfaces()) { _resolveBindings(intType); } } @Override public String toString() { if (_bindings == null) { _resolve(); } StringBuilder sb = new StringBuilder("[TypeBindings for "); if (_contextType != null) { sb.append(_contextType.toString()); } else { sb.append(_contextClass.getName()); } sb.append(": ").append(_bindings).append("]"); return sb.toString(); } }