| 
001 /*002  * Copyright 2008-2015 the original author or authors.
 003  *
 004  * Licensed under the Apache License, Version 2.0 (the "License");
 005  * you may not use this file except in compliance with the License.
 006  * You may obtain a copy of the License at
 007  *
 008  *     http://www.apache.org/licenses/LICENSE-2.0
 009  *
 010  * Unless required by applicable law or agreed to in writing, software
 011  * distributed under the License is distributed on an "AS IS" BASIS,
 012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 013  * See the License for the specific language governing permissions and
 014  * limitations under the License.
 015  */
 016 package griffon.util;
 017
 018 import javax.annotation.Nonnull;
 019 import javax.annotation.Nullable;
 020 import java.lang.reflect.Method;
 021 import java.lang.reflect.Modifier;
 022 import java.util.Arrays;
 023
 024 import static griffon.util.GriffonNameUtils.requireNonBlank;
 025 import static java.util.Objects.requireNonNull;
 026
 027 /**
 028  * @author Andres Almiray
 029  */
 030 public class MethodDescriptor implements Comparable<MethodDescriptor> {
 031     private final String methodName;
 032     private final String[] paramTypes;
 033     private final int hashCode;
 034     private final int modifiers;
 035
 036     private static final String[] EMPTY_CLASS_PARAMETERS = new String[0];
 037
 038     @Nonnull
 039     public static MethodDescriptor forMethod(@Nonnull Method method) {
 040         return forMethod(method, false);
 041     }
 042
 043     @Nonnull
 044     public static MethodDescriptor forMethod(@Nonnull Method method, boolean removeAbstractModifier) {
 045         requireNonNull(method, "Argument 'method' must not be null");
 046         int modifiers = method.getModifiers();
 047         if (removeAbstractModifier) {
 048             modifiers -= Modifier.ABSTRACT;
 049         }
 050         return new MethodDescriptor(method.getName(), method.getParameterTypes(), modifiers);
 051     }
 052
 053     private static boolean areParametersCompatible(String[] params1, String[] params2) {
 054         if (params1.length != params2.length) {
 055             return false;
 056         }
 057
 058         for (int i = 0; i < params1.length; i++) {
 059             String p1 = params1[i];
 060             String p2 = params2[i];
 061             if (!p1.equals(p2) && !GriffonClassUtils.isMatchBetweenPrimitiveAndWrapperTypes(p1, p2)) {
 062                 return false;
 063             }
 064         }
 065
 066         return true;
 067     }
 068
 069     public MethodDescriptor(@Nonnull String methodName) {
 070         this(methodName, EMPTY_CLASS_PARAMETERS, Modifier.PUBLIC);
 071     }
 072
 073     public MethodDescriptor(@Nonnull String methodName, int modifiers) {
 074         this(methodName, EMPTY_CLASS_PARAMETERS, modifiers);
 075     }
 076
 077     public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes) {
 078         this(methodName, paramTypes, Modifier.PUBLIC);
 079     }
 080
 081     public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes) {
 082         this(methodName, paramTypes, Modifier.PUBLIC);
 083     }
 084
 085     public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes, int modifiers) {
 086         this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank");
 087         requireNonNull(paramTypes, "Argument 'paramTypes' must not be null");
 088
 089         this.paramTypes = new String[paramTypes.length];
 090         for (int i = 0; i < paramTypes.length; i++) {
 091             this.paramTypes[i] = paramTypes[i].getName();
 092         }
 093
 094         this.modifiers = modifiers;
 095         this.hashCode = 31 * methodName.hashCode() + modifiers;
 096     }
 097
 098     public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes, int modifiers) {
 099         this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank");
 100         requireNonNull(paramTypes, "Argument 'paramTypes' must not be null");
 101
 102         this.paramTypes = Arrays.copyOf(paramTypes, paramTypes.length);
 103
 104         this.modifiers = modifiers;
 105         this.hashCode = 31 * methodName.hashCode() + modifiers;
 106     }
 107
 108     @Nonnull
 109     public String getName() {
 110         return methodName;
 111     }
 112
 113     @Nonnull
 114     public String[] getParameterTypes() {
 115         return paramTypes;
 116     }
 117
 118     public int getModifiers() {
 119         return modifiers;
 120     }
 121
 122     public boolean equals(Object obj) {
 123         if (!(obj instanceof MethodDescriptor)) {
 124             return false;
 125         }
 126         MethodDescriptor md = (MethodDescriptor) obj;
 127
 128         return methodName.equals(md.methodName) &&
 129             modifiers == md.modifiers &&
 130             areParametersCompatible(paramTypes, md.paramTypes);
 131     }
 132
 133     public int hashCode() {
 134         return hashCode;
 135     }
 136
 137     public String toString() {
 138         StringBuilder b = new StringBuilder();
 139         b.append(Modifier.toString(modifiers)).append(" ");
 140         b.append(methodName).append("(");
 141         for (int i = 0; i < paramTypes.length; i++) {
 142             if (i != 0) b.append(", ");
 143             b.append(paramTypes[i]);
 144         }
 145         b.append(")");
 146         return b.toString();
 147     }
 148
 149     public int compareTo(MethodDescriptor md) {
 150         int c = methodName.compareTo(md.methodName);
 151         if (c != 0) return c;
 152         c = modifiers - md.modifiers;
 153         if (c != 0) return c;
 154         c = paramTypes.length - md.paramTypes.length;
 155         if (c != 0) return c;
 156         for (int i = 0; i < paramTypes.length; i++) {
 157             c = paramTypes[i].compareTo(md.paramTypes[i]);
 158             if (c != 0) return c;
 159         }
 160
 161         return 0;
 162     }
 163
 164     public boolean matches(@Nonnull MethodDescriptor md) {
 165         requireNonNull(md, "Argument 'methodDescriptor' must not be null");
 166         if (!methodName.equals(md.methodName) ||
 167             modifiers != md.modifiers ||
 168             paramTypes.length != md.paramTypes.length) {
 169             return false;
 170         }
 171
 172         for (int i = 0; i < paramTypes.length; i++) {
 173             Class<?> param1 = loadClass(paramTypes[i]);
 174             Class<?> param2 = loadClass(md.paramTypes[i]);
 175             if (param1 != null && param2 != null && !GriffonClassUtils.isAssignableOrConvertibleFrom(param1, param2)) {
 176                 return false;
 177             }
 178         }
 179
 180         return true;
 181     }
 182
 183     @Nullable
 184     private Class<?> loadClass(@Nonnull String classname) {
 185         try {
 186             return Class.forName(classname, true, getClass().getClassLoader());
 187         } catch (ClassNotFoundException e) {
 188             if (GriffonClassUtils.PRIMITIVE_TYPE_COMPATIBLE_TYPES.containsKey(classname)) {
 189                 try {
 190                     classname = GriffonClassUtils.PRIMITIVE_TYPE_COMPATIBLE_TYPES.get(classname);
 191                     return Class.forName(classname, true, getClass().getClassLoader());
 192                 } catch (ClassNotFoundException e1) {
 193                     return null;
 194                 }
 195             } else {
 196                 return null;
 197             }
 198         }
 199     }
 200 }
 |