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    }