// 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.apache.tapestry5.ioc.internal.util; import org.apache.tapestry5.plastic.PlasticUtils; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; /** * Used to search from a particular class up the inheritance hierarchy of extended classes and implemented interfaces. * * The search starts with the initial class (provided in the constructor). It progresses up the inheritance chain, but * skips java.lang.Object. * * Once classes are exhausted, the inheritance hierarchy is searched. This is a breadth-first search, rooted in the * interfaces implemented by the initial class at its super classes. * * Once all interfaces are exhausted, java.lang.Object is returned (it is always returned last). * * Two minor tweak to normal inheritance rules: <ul> <li> Normally, the parent class of an <em>object</em> array is * java.lang.Object, which is odd because FooService[] is assignable to Object[]. Thus, we tweak the search so that the * effective super class of FooService[] is Object[]. <li> The "super class" of a primtive type is its <em>wrapper type</em>, * with the exception of void, whose "super class" is left at its normal value (Object.class) </ul> * * This class implements the {@link Iterable} interface, so it can be used directly in a for loop: <code> for (Class * search : new InheritanceSearch(startClass)) { ... } </code> * * This class is not thread-safe. */ public class InheritanceSearch implements Iterator<Class>, Iterable<Class> { private Class searchClass; private final Set<Class> addedInterfaces = CollectionFactory.newSet(); private final LinkedList<Class> interfaceQueue = CollectionFactory.newLinkedList(); private enum State { CLASS, INTERFACE, DONE } private State state; public InheritanceSearch(Class searchClass) { this.searchClass = searchClass; queueInterfaces(searchClass); state = searchClass == Object.class ? State.INTERFACE : State.CLASS; } private void queueInterfaces(Class searchClass) { for (Class intf : searchClass.getInterfaces()) { if (addedInterfaces.contains(intf)) continue; interfaceQueue.addLast(intf); addedInterfaces.add(intf); } } @Override public Iterator<Class> iterator() { return this; } @Override public boolean hasNext() { return state != State.DONE; } @Override public Class next() { switch (state) { case CLASS: Class result = searchClass; searchClass = parentOf(searchClass); if (searchClass == null) state = State.INTERFACE; else queueInterfaces(searchClass); return result; case INTERFACE: if (interfaceQueue.isEmpty()) { state = State.DONE; return Object.class; } Class intf = interfaceQueue.removeFirst(); queueInterfaces(intf); return intf; default: throw new IllegalStateException(); } } /** * Returns the parent of the given class. Tweaks inheritance for object arrays. Returns null instead of * Object.class. */ private Class parentOf(Class clazz) { if (clazz != void.class && clazz.isPrimitive()) return PlasticUtils.toWrapperType(clazz); if (clazz.isArray() && clazz != Object[].class) { Class componentType = clazz.getComponentType(); while (componentType.isArray()) componentType = componentType.getComponentType(); if (!componentType.isPrimitive()) return Object[].class; } Class parent = clazz.getSuperclass(); return parent != Object.class ? parent : null; } /** * @throws UnsupportedOperationException * always */ @Override public void remove() { throw new UnsupportedOperationException(); } }