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}