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