/*
* Copyright 2014 the original author or authors.
*
* Licensed 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.gradle.tooling.internal.adapter;
import net.jcip.annotations.ThreadSafe;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ThreadSafe
class TypeInspector {
private final Set<Class<?>> stopAt = new HashSet<Class<?>>();
private final Map<Class<?>, Set<Class<?>>> inspected = new HashMap<Class<?>, Set<Class<?>>>();
public TypeInspector() {
stopAt.add(List.class);
stopAt.add(Set.class);
stopAt.add(Collection.class);
stopAt.add(Map.class);
}
/**
* Returns all interfaces reachable from the given interface, including the interface itself.
*/
public Set<Class<?>> getReachableTypes(Class<?> type) {
Set<Class<?>> types = inspected.get(type);
if (types == null) {
types = new HashSet<Class<?>>();
visit(type, types);
inspected.put(type, types);
}
return types;
}
private void visit(Class<?> type, Set<Class<?>> types) {
if (type.isArray()) {
visit(type.getComponentType(), types);
return;
}
if (!type.isInterface() || !types.add(type) || stopAt.contains(type)) {
return;
}
for (Type superType : type.getGenericInterfaces()) {
visit(superType, types);
}
for (Method method : type.getDeclaredMethods()) {
visit(method.getGenericReturnType(), types);
for (TypeVariable<Method> typeVariable : method.getTypeParameters()) {
visit(typeVariable, types);
}
}
}
private void visit(Type type, Set<Class<?>> types) {
if (type instanceof Class) {
visit((Class) type, types);
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
visit(parameterizedType.getRawType(), types);
for (Type typeArg : parameterizedType.getActualTypeArguments()) {
visit(typeArg, types);
}
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
for (Type bound : wildcardType.getUpperBounds()) {
visit(bound, types);
}
for (Type bound : wildcardType.getLowerBounds()) {
visit(bound, types);
}
} else if (type instanceof GenericArrayType) {
GenericArrayType arrayType = (GenericArrayType) type;
visit(arrayType.getGenericComponentType(), types);
} else if (type instanceof TypeVariable) {
TypeVariable<?> typeVariable = (TypeVariable) type;
for (Type bound : typeVariable.getBounds()) {
visit(bound, types);
}
}
}
}