    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    }