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