1/*
2 * Copyright (C) 2013 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.reflect;
18
19import com.google.common.collect.Sets;
20
21import java.lang.reflect.GenericArrayType;
22import java.lang.reflect.ParameterizedType;
23import java.lang.reflect.Type;
24import java.lang.reflect.TypeVariable;
25import java.lang.reflect.WildcardType;
26import java.util.Set;
27
28import javax.annotation.concurrent.NotThreadSafe;
29
30/**
31 * Based on what a {@link Type} is, dispatch it to the corresponding {@code visit*} method. By
32 * default, no recursion is done for type arguments or type bounds. But subclasses can opt to do
33 * recursion by calling {@link #visit} for any {@code Type} while visitation is in progress. For
34 * example, this can be used to reject wildcards or type variables contained in a type as in:
35 *
36 * <pre>   {@code
37 *   new TypeVisitor() {
38 *     protected void visitParameterizedType(ParameterizedType t) {
39 *       visit(t.getOwnerType());
40 *       visit(t.getActualTypeArguments());
41 *     }
42 *     protected void visitGenericArrayType(GenericArrayType t) {
43 *       visit(t.getGenericComponentType());
44 *     }
45 *     protected void visitTypeVariable(TypeVariable<?> t) {
46 *       throw new IllegalArgumentException("Cannot contain type variable.");
47 *     }
48 *     protected void visitWildcardType(WildcardType t) {
49 *       throw new IllegalArgumentException("Cannot contain wildcard type.");
50 *     }
51 *   }.visit(type);}</pre>
52 *
53 * <p>One {@code Type} is visited at most once. The second time the same type is visited, it's
54 * ignored by {@link #visit}. This avoids infinite recursion caused by recursive type bounds.
55 *
56 * <p>This class is <em>not</em> thread safe.
57 *
58 * @author Ben Yu
59 */
60@NotThreadSafe
61abstract class TypeVisitor {
62
63  private final Set<Type> visited = Sets.newHashSet();
64
65  /**
66   * Visits the given types. Null types are ignored. This allows subclasses to call
67   * {@code visit(parameterizedType.getOwnerType())} safely without having to check nulls.
68   */
69  public final void visit(Type... types) {
70    for (Type type : types) {
71      if (type == null || !visited.add(type)) {
72        // null owner type, or already visited;
73        continue;
74      }
75      boolean succeeded = false;
76      try {
77        if (type instanceof TypeVariable) {
78          visitTypeVariable((TypeVariable<?>) type);
79        } else if (type instanceof WildcardType) {
80          visitWildcardType((WildcardType) type);
81        } else if (type instanceof ParameterizedType) {
82          visitParameterizedType((ParameterizedType) type);
83        } else if (type instanceof Class) {
84          visitClass((Class<?>) type);
85        } else if (type instanceof GenericArrayType) {
86          visitGenericArrayType((GenericArrayType) type);
87        } else {
88          throw new AssertionError("Unknown type: " + type);
89        }
90        succeeded = true;
91      } finally {
92        if (!succeeded) { // When the visitation failed, we don't want to ignore the second.
93          visited.remove(type);
94        }
95      }
96    }
97  }
98
99  void visitClass(Class<?> t) {}
100  void visitGenericArrayType(GenericArrayType t) {}
101  void visitParameterizedType(ParameterizedType t) {}
102  void visitTypeVariable(TypeVariable<?> t) {}
103  void visitWildcardType(WildcardType t) {}
104}
105