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.tapestry.ioc.internal.util;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newLinkedList;
018: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newSet;
019:
020: import java.util.Iterator;
021: import java.util.LinkedList;
022: import java.util.Set;
023:
024: import org.apache.tapestry.ioc.services.ClassFabUtils;
025:
026: /**
027: * Used to search from a particular class up the inheritance hierarchy of extended classes and
028: * implemented interfaces.
029: * <p>
030: * The search starts with the initial class (provided in the constructor). It progresses up the
031: * inheritance chain, but skips java.lang.Object.
032: * <p>
033: * Once classes are exhausted, the inheritance hiearchy is searched. This is a breadth-first search,
034: * rooted in the interfaces implemented by the initial class at its super classes.
035: * <p>
036: * Once all interfaces are exhausted, java.lang.Object is returned (it is always returned last).
037: * <p>
038: * Two minor tweak to normal inheritance rules:
039: * <ul>
040: * <li> Normally, the parent class of an <em>object</em> array is java.lang.Object, which is odd
041: * because Foo[] is assignable to Object[]. Thus, we tweak the search so that the effective super
042: * class of Foo[] is Object[].
043: * <li> The "super class" of a primtive type is its <em>wrapper type</em>, with the exception of
044: * void, whose "super class" is left at its normal value (Object.class)
045: * </ul>
046: * <p>
047: * This class implements the {@link Iterable} interface, so it can be used directly in a for loop:
048: * <code>
049: * for (Class search : new InheritanceSearch(startClass)) {
050: * ...
051: * }
052: * </code>
053: * <p>
054: * This class is not threadsafe.
055: */
056: public class InheritanceSearch implements Iterator<Class>,
057: Iterable<Class> {
058: private Class _searchClass;
059:
060: private final Set<Class> _addedInterfaces = newSet();
061:
062: private final LinkedList<Class> _interfaceQueue = newLinkedList();
063:
064: private enum State {
065: CLASS, INTERFACE, DONE;
066: }
067:
068: private State _state;
069:
070: public InheritanceSearch(Class searchClass) {
071: _searchClass = searchClass;
072:
073: queueInterfaces(searchClass);
074:
075: _state = searchClass == Object.class ? State.INTERFACE
076: : State.CLASS;
077: }
078:
079: private void queueInterfaces(Class searchClass) {
080: for (Class intf : searchClass.getInterfaces()) {
081: if (_addedInterfaces.contains(intf))
082: continue;
083:
084: _interfaceQueue.addLast(intf);
085: _addedInterfaces.add(intf);
086: }
087: }
088:
089: public Iterator<Class> iterator() {
090: return this ;
091: }
092:
093: public boolean hasNext() {
094: return _state != State.DONE;
095: }
096:
097: public Class next() {
098: switch (_state) {
099: case CLASS:
100:
101: Class result = _searchClass;
102:
103: _searchClass = parentOf(_searchClass);
104:
105: if (_searchClass == null)
106: _state = State.INTERFACE;
107: else
108: queueInterfaces(_searchClass);
109:
110: return result;
111:
112: case INTERFACE:
113:
114: if (_interfaceQueue.isEmpty()) {
115: _state = State.DONE;
116: return Object.class;
117: }
118:
119: Class intf = _interfaceQueue.removeFirst();
120:
121: queueInterfaces(intf);
122:
123: return intf;
124:
125: default:
126: throw new IllegalStateException();
127: }
128:
129: }
130:
131: /**
132: * Returns the parent of the given class. Tweaks inheritance for object arrays. Returns null
133: * instead of Object.class.
134: */
135: private Class parentOf(Class clazz) {
136: if (clazz != void.class && clazz.isPrimitive())
137: return ClassFabUtils.getWrapperType(clazz);
138:
139: if (clazz.isArray() && clazz != Object[].class) {
140: Class componentType = clazz.getComponentType();
141:
142: while (componentType.isArray())
143: componentType = componentType.getComponentType();
144:
145: if (!componentType.isPrimitive())
146: return Object[].class;
147: }
148:
149: Class parent = clazz.getSuperclass();
150:
151: return parent != Object.class ? parent : null;
152: }
153:
154: /**
155: * @throws UnsupportedOperationException
156: * always
157: */
158: public void remove() {
159: throw new UnsupportedOperationException();
160: }
161:
162: }
|