    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}