001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.commons.lang;
021
022import java.lang.reflect.Method;
023import java.lang.reflect.Modifier;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.isis.core.metamodel.methodutils.MethodScope;
028
029public class MethodUtil {
030    
031    private MethodUtil(){}
032
033    public static void invoke(final List<Method> methods, final Object object) {
034        for (final Method method : methods) {
035            MethodExtensions.invoke(method, object);
036        }
037    }
038
039    /**
040     * Searches the supplied array of methods for specific method and returns
041     * it, also removing it from supplied array if found (by setting to
042     * <tt>null</tt>).
043     * 
044     * <p>
045     * Any methods that do not meet the search criteria are left in the array of
046     * methods.
047     * 
048     * <p>
049     * The search algorithm is as specified in
050     * {@link MethodUtil#findMethodIndex(List, MethodScope, String, Class, Class[])}.
051     */
052    public static Method removeMethod(final List<Method> methods, final MethodScope methodScope, final String name, final Class<?> returnType, final Class<?>[] paramTypes) {
053        final int idx = MethodUtil.findMethodIndex(methods, methodScope, name, returnType, paramTypes);
054        if (idx != -1) {
055            final Method method = methods.get(idx);
056            methods.set(idx, null);
057            return method;
058        }
059        return null;
060    }
061
062    /**
063     * Searches the supplied array of methods for specific method and returns
064     * its index, otherwise returns <tt>-1</tt>.
065     * 
066     * <p>
067     * The search algorithm is:
068     * <ul>
069     * <li>has the specified prefix</li>
070     * <li>has the specified return type, or <tt>void</tt> if canBeVoid is
071     * <tt>true</tt> (but see below)</li>
072     * <li>has the specified number of parameters</li>
073     * </ul>
074     * If the returnType is specified as null then the return type is ignored.
075     */
076    public static int findMethodIndex(final List<Method> methods, final MethodScope methodScope, final String name, final Class<?> returnType, final Class<?>[] paramTypes) {
077        int idx = -1;
078        method: for (int i = 0; i < methods.size(); i++) {
079            if (methods.get(i) == null) {
080                continue;
081            }
082    
083            final Method method = methods.get(i);
084            final int modifiers = method.getModifiers();
085    
086            // check for public modifier
087            if (!Modifier.isPublic(modifiers)) {
088                continue;
089            }
090    
091            // check for static modifier
092            if (!inScope(method, methodScope)) {
093                continue;
094            }
095    
096            // check for name
097            if (!method.getName().equals(name)) {
098                continue;
099            }
100    
101            // check for return type
102            if (returnType != null && returnType != method.getReturnType()) {
103                continue;
104            }
105    
106            // check params (if required)
107            if (paramTypes != null) {
108                final Class<?>[] parameterTypes = method.getParameterTypes();
109                if (paramTypes.length != parameterTypes.length) {
110                    continue;
111                }
112    
113                for (int c = 0; c < paramTypes.length; c++) {
114                    if ((paramTypes[c] != null) && (paramTypes[c] != parameterTypes[c])) {
115                        continue method;
116                    }
117                }
118            }
119            idx = i;
120            break;
121        }
122        return idx;
123    }
124
125    public static boolean inScope(final Method extendee, final MethodScope methodScope) {
126        final boolean isStatic = MethodExtensions.isStatic(extendee);
127        return isStatic && methodScope == MethodScope.CLASS || !isStatic && methodScope == MethodScope.OBJECT;
128    }
129
130    /**
131     * Searches the supplied array of methods for all specific methods and
132     * returns them, also removing them from supplied array if found.
133     * 
134     * <p>
135     * Any methods that do not meet the search criteria are left in the array of
136     * methods.
137     * 
138     * <p>
139     * The search algorithm is:
140     * <ul>
141     * <li>has the specified prefix</li>
142     * <li>has the specified return type, or <tt>void</tt> if canBeVoid is
143     * <tt>true</tt> (but see below)</li>
144     * <li>has the specified number of parameters</li>
145     * </ul>
146     * If the returnType is specified as null then the return type is ignored.
147     * 
148     * @param forClass
149     * @param name
150     * @param returnType
151     * @param paramTypes
152     *            the set of parameters the method should have, if null then is
153     *            ignored
154     * @return Method
155     */
156    public static List<Method> removeMethods(final List<Method> methods, final MethodScope forClass, final String prefix, final Class<?> returnType, final boolean canBeVoid, final int paramCount) {
157    
158        final List<Method> validMethods = new ArrayList<Method>();
159    
160        for (int i = 0; i < methods.size(); i++) {
161            if (methods.get(i) == null) {
162                continue;
163            }
164    
165            final Method method = methods.get(i);
166    
167            if (!inScope(method, forClass)) {
168                continue;
169            }
170    
171            final boolean goodPrefix = method.getName().startsWith(prefix);
172    
173            final boolean goodCount = method.getParameterTypes().length == paramCount;
174            final Class<?> type = method.getReturnType();
175            final boolean goodReturn = ClassExtensions.isCompatibleAsReturnType(returnType, canBeVoid, type);
176    
177            if (goodPrefix && goodCount && goodReturn) {
178                validMethods.add(method);
179                methods.set(i, null);
180            }
181        }
182        return validMethods;
183    }
184
185    /**
186     * From the supplied method array, finds but <i>does not remove</i> methods
187     * that have the required prefix, and adds to the supplied candidates
188     * vector.
189     */
190    public static void findPrefixedInstanceMethods(final Method[] methods, final String prefix, final List<Method> candidates) {
191        for (final Method method : methods) {
192            if (method == null) {
193                continue;
194            }
195    
196            if (MethodExtensions.isStatic(method)) {
197                continue;
198            }
199    
200            final boolean goodPrefix = method.getName().startsWith(prefix);
201            final boolean goodCount = method.getParameterTypes().length == 0;
202    
203            if (goodPrefix && goodCount) {
204                candidates.add(method);
205            }
206        }
207    }
208
209}