001/**
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.isis.core.progmodel.facets;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Method;
021import java.lang.reflect.Modifier;
022import java.util.ArrayList;
023import java.util.List;
024
025import org.apache.isis.core.metamodel.facetapi.MethodRemover;
026import org.apache.isis.core.metamodel.methodutils.MethodScope;
027
028public final class MethodFinderUtils {
029
030    private MethodFinderUtils() {
031    }
032
033    public static Method findMethodWithOrWithoutParameters(final Class<?> type, final MethodScope classMethod, final String name, final Class<?> returnType, final Class<?>[] paramTypes) {
034        Method method = MethodFinderUtils.findMethod(type, classMethod, name, returnType, paramTypes);
035        if (method == null) {
036            method = MethodFinderUtils.findMethod(type, classMethod, name, returnType, MethodPrefixBasedFacetFactoryAbstract.NO_PARAMETERS_TYPES);
037        }
038        return method;
039    }
040
041    /**
042     * Returns a specific public methods that: have the specified prefix; have
043     * the specified return type (or some subtype), and has the
044     * specified number of parameters.
045     * 
046     * <p>
047     * If the returnType is specified as null then the return type is ignored.
048     * If void.class is passed in, then searches for void methods.
049     * 
050     * <p>
051     * If the parameter type array is null, is also not checked.
052     */
053    public static Method findMethod(
054            final Class<?> type, 
055            final MethodScope methodScope, 
056            final String name, 
057            final Class<?> returnType, 
058            final Class<?>[] paramTypes) {
059        
060        Method method;
061        try {
062            method = type.getMethod(name, paramTypes);
063        } catch (final SecurityException e) {
064            return null;
065        } catch (final NoSuchMethodException e) {
066            return null;
067        }
068
069        final int modifiers = method.getModifiers();
070
071        if (!Modifier.isPublic(modifiers)) {
072            return null;
073        }
074
075        // check for scope modifier
076        if (!methodScope.matchesScopeOf(method)) {
077            return null;
078        }
079
080        if (!method.getName().equals(name)) {
081            return null;
082        }
083
084        if (returnType != null && !returnType.isAssignableFrom(method.getReturnType())) {
085            return null;
086        }
087
088        if (paramTypes != null) {
089            final Class<?>[] parameterTypes = method.getParameterTypes();
090            if (paramTypes.length != parameterTypes.length) {
091                return null;
092            }
093
094            for (int c = 0; c < paramTypes.length; c++) {
095                if ((paramTypes[c] != null) && (paramTypes[c] != parameterTypes[c])) {
096                    return null;
097                }
098            }
099        }
100
101        return method;
102    }
103
104    protected static boolean doesNotMatchScope(final MethodScope methodScope, final Method method) {
105        return methodScope.doesNotMatchScope(method);
106    }
107
108    public static Method findMethod(final Class<?> type, final MethodScope methodScope, final String name, final Class<?> returnType) {
109        try {
110            final Method[] methods = type.getMethods();
111            for (final Method method2 : methods) {
112                final Method method = method2;
113                final int modifiers = method.getModifiers();
114                // check for public modifier
115                if (!Modifier.isPublic(modifiers)) {
116                    continue;
117                }
118
119                // check correct scope (static vs instance)
120                if (!methodScope.matchesScopeOf(method)) {
121                    continue;
122                }
123
124                // check for name
125                if (!method.getName().equals(name)) {
126                    continue;
127                }
128
129                // check for return type
130                if (returnType != null && returnType != method.getReturnType()) {
131                    continue;
132                }
133                return method;
134            }
135        } catch (final SecurityException e) {
136            return null;
137        }
138        return null;
139    }
140
141    public static List<Method> findMethodsWithAnnotation(final Class<?> type, final MethodScope methodScope, final Class<? extends Annotation> annotationClass) {
142
143        final List<Method> methods = new ArrayList<Method>();
144
145        // Validate arguments
146        if ((type == null) || (methodScope == null) || (annotationClass == null)) {
147            throw new IllegalArgumentException("One or more arguments are 'null' valued");
148        }
149
150        // Find methods annotated with the specified annotation
151        for (final Method method : type.getMethods()) {
152            if (!methodScope.matchesScopeOf(method)) {
153                continue;
154            }
155
156            if (method.isAnnotationPresent(annotationClass)) {
157                methods.add(method);
158            }
159        }
160
161        return methods;
162    }
163
164    public static void removeMethod(final MethodRemover methodRemover, final Method method) {
165        if (methodRemover != null && method != null) {
166            methodRemover.removeMethod(method);
167        }
168    }
169
170    public static Class<?>[] paramTypesOrNull(final Class<?> type) {
171        return type == null ? null : new Class[] { type };
172    }
173
174    public static boolean allParametersOfSameType(final Class<?>[] params) {
175        final Class<?> firstParam = params[0];
176        for (int i = 1; i < params.length; i++) {
177            if (params[i] != firstParam) {
178                return false;
179            }
180        }
181        return true;
182    }
183
184}