001 // Copyright 2006 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.internal.util;
016
017 import org.apache.tapestry5.plastic.PlasticUtils;
018
019 import java.util.Iterator;
020 import java.util.LinkedList;
021 import java.util.Set;
022
023 /**
024 * Used to search from a particular class up the inheritance hierarchy of extended classes and implemented interfaces.
025 * <p/>
026 * The search starts with the initial class (provided in the constructor). It progresses up the inheritance chain, but
027 * skips java.lang.Object.
028 * <p/>
029 * Once classes are exhausted, the inheritance hiearchy is searched. This is a breadth-first search, rooted in the
030 * interfaces implemented by the initial class at its super classes.
031 * <p/>
032 * Once all interfaces are exhausted, java.lang.Object is returned (it is always returned last).
033 * <p/>
034 * Two minor tweak to normal inheritance rules: <ul> <li> Normally, the parent class of an <em>object</em> array is
035 * java.lang.Object, which is odd because Foo[] is assignable to Object[]. Thus, we tweak the search so that the
036 * effective super class of Foo[] is Object[]. <li> The "super class" of a primtive type is its <em>wrapper type</em>,
037 * with the exception of void, whose "super class" is left at its normal value (Object.class) </ul>
038 * <p/>
039 * This class implements the {@link Iterable} interface, so it can be used directly in a for loop: <code> for (Class
040 * search : new InheritanceSearch(startClass)) { ... } </code>
041 * <p/>
042 * This class is not threadsafe.
043 */
044 public class InheritanceSearch implements Iterator<Class>, Iterable<Class>
045 {
046 private Class searchClass;
047
048 private final Set<Class> addedInterfaces = CollectionFactory.newSet();
049
050 private final LinkedList<Class> interfaceQueue = CollectionFactory.newLinkedList();
051
052 private enum State
053 {
054 CLASS, INTERFACE, DONE
055 }
056
057 private State state;
058
059 public InheritanceSearch(Class searchClass)
060 {
061 this.searchClass = searchClass;
062
063 queueInterfaces(searchClass);
064
065 state = searchClass == Object.class ? State.INTERFACE : State.CLASS;
066 }
067
068 private void queueInterfaces(Class searchClass)
069 {
070 for (Class intf : searchClass.getInterfaces())
071 {
072 if (addedInterfaces.contains(intf)) continue;
073
074 interfaceQueue.addLast(intf);
075 addedInterfaces.add(intf);
076 }
077 }
078
079 public Iterator<Class> iterator()
080 {
081 return this;
082 }
083
084 public boolean hasNext()
085 {
086 return state != State.DONE;
087 }
088
089 public Class next()
090 {
091 switch (state)
092 {
093 case CLASS:
094
095 Class result = searchClass;
096
097 searchClass = parentOf(searchClass);
098
099 if (searchClass == null) state = State.INTERFACE;
100 else queueInterfaces(searchClass);
101
102 return result;
103
104 case INTERFACE:
105
106 if (interfaceQueue.isEmpty())
107 {
108 state = State.DONE;
109 return Object.class;
110 }
111
112 Class intf = interfaceQueue.removeFirst();
113
114 queueInterfaces(intf);
115
116 return intf;
117
118 default:
119 throw new IllegalStateException();
120 }
121
122 }
123
124 /**
125 * Returns the parent of the given class. Tweaks inheritance for object arrays. Returns null instead of
126 * Object.class.
127 */
128 private Class parentOf(Class clazz)
129 {
130 if (clazz != void.class && clazz.isPrimitive()) return PlasticUtils.toWrapperType(clazz);
131
132 if (clazz.isArray() && clazz != Object[].class)
133 {
134 Class componentType = clazz.getComponentType();
135
136 while (componentType.isArray()) componentType = componentType.getComponentType();
137
138 if (!componentType.isPrimitive()) return Object[].class;
139 }
140
141 Class parent = clazz.getSuperclass();
142
143 return parent != Object.class ? parent : null;
144 }
145
146 /**
147 * @throws UnsupportedOperationException always
148 */
149 public void remove()
150 {
151 throw new UnsupportedOperationException();
152 }
153
154 }