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 015package org.apache.tapestry5.ioc.internal.util; 016 017import org.apache.tapestry5.plastic.PlasticUtils; 018 019import java.util.Iterator; 020import java.util.LinkedList; 021import 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 hierarchy 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 FooService[] is assignable to Object[]. Thus, we tweak the search so that the 036 * effective super class of FooService[] 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 thread-safe. 043 */ 044public 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 @Override 080 public Iterator<Class> iterator() 081 { 082 return this; 083 } 084 085 @Override 086 public boolean hasNext() 087 { 088 return state != State.DONE; 089 } 090 091 @Override 092 public Class next() 093 { 094 switch (state) 095 { 096 case CLASS: 097 098 Class result = searchClass; 099 100 searchClass = parentOf(searchClass); 101 102 if (searchClass == null) state = State.INTERFACE; 103 else queueInterfaces(searchClass); 104 105 return result; 106 107 case INTERFACE: 108 109 if (interfaceQueue.isEmpty()) 110 { 111 state = State.DONE; 112 return Object.class; 113 } 114 115 Class intf = interfaceQueue.removeFirst(); 116 117 queueInterfaces(intf); 118 119 return intf; 120 121 default: 122 throw new IllegalStateException(); 123 } 124 125 } 126 127 /** 128 * Returns the parent of the given class. Tweaks inheritance for object arrays. Returns null instead of 129 * Object.class. 130 */ 131 private Class parentOf(Class clazz) 132 { 133 if (clazz != void.class && clazz.isPrimitive()) return PlasticUtils.toWrapperType(clazz); 134 135 if (clazz.isArray() && clazz != Object[].class) 136 { 137 Class componentType = clazz.getComponentType(); 138 139 while (componentType.isArray()) componentType = componentType.getComponentType(); 140 141 if (!componentType.isPrimitive()) return Object[].class; 142 } 143 144 Class parent = clazz.getSuperclass(); 145 146 return parent != Object.class ? parent : null; 147 } 148 149 /** 150 * @throws UnsupportedOperationException 151 * always 152 */ 153 @Override 154 public void remove() 155 { 156 throw new UnsupportedOperationException(); 157 } 158 159}