001// Copyright 2004, 2005, 2006, 2011, 2012 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.services;
016
017import java.lang.reflect.Method;
018import java.util.*;
019
020import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
021import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
022
023/**
024 * Utility used to iterate over the publically visible methods of a class or interface. The MethodIterator understands
025 * some complications that can occur when a class inherits the same method from multiple interfaces and with slightly
026 * different signatures (due to the fact that declared thrown exceptions can vary slightly for the "same" method).
027 *
028 * @see MethodSignature#isOverridingSignatureOf(MethodSignature)
029 */
030public class MethodIterator
031{
032    private boolean toString;
033
034    private int index = 0;
035
036    private final int count;
037
038    private final List<MethodSignature> signatures;
039
040    private static final Comparator<MethodSignature> COMPARATOR = new Comparator<MethodSignature>()
041    {
042        public int compare(MethodSignature o1, MethodSignature o2)
043        {
044
045            return o1.getName().compareTo(o2.getName());
046        }
047    };
048
049    public MethodIterator(Class subjectClass)
050    {
051        Method[] methods = subjectClass.getMethods();
052
053        Map<String, MethodSignature> map = newMap();
054
055        for (int i = 0; i < methods.length; i++)
056            processMethod(methods[i], map);
057
058        signatures = newList(map.values());
059        count = signatures.size();
060
061        Collections.sort(signatures, COMPARATOR);
062    }
063
064    /**
065     * Returns true if the method is the standard toString() method. Very few interfaces will ever include this method
066     * as part of the interface, but we have to be sure.
067     */
068    public static boolean isToString(Method method)
069    {
070        if (!method.getName().equals("toString"))
071            return false;
072
073        if (method.getParameterTypes().length > 0)
074            return false;
075
076        return method.getReturnType().equals(String.class);
077    }
078
079    private void processMethod(Method m, Map<String, MethodSignature> map)
080    {
081        toString |= isToString(m);
082
083        MethodSignature sig = new MethodSignature(m);
084        String uid = sig.getUniqueId();
085
086        MethodSignature existing = map.get(uid);
087
088        if (existing == null || sig.isOverridingSignatureOf(existing))
089            map.put(uid, sig);
090    }
091
092    public boolean hasNext()
093    {
094        return index < count;
095    }
096
097    /**
098     * Returns the next method (as a {@link MethodSignature}, returning null when all are exhausted. Each method
099     * signature is returned exactly once (even if the same method signature is defined in multiple inherited classes or
100     * interfaces). The method signatures returned in ascending order, according to the "natural ordering".
101     *
102     * @throws NoSuchElementException
103     *         if there are no more signatures
104     */
105    public MethodSignature next()
106    {
107        if (index >= count)
108            throw new NoSuchElementException();
109
110        return signatures.get(index++);
111    }
112
113    /**
114     * Returns true if the method <code>public String toString()</code> is part of the interface. This will be known
115     * immediately after iterator contruction (it is not necessary to iterate the methods first).
116     */
117    public boolean getToString()
118    {
119        return toString;
120    }
121}