MethodDescriptor.java
001 /*
002  * Copyright 2008-2016 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 = (MethodDescriptorobj;
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 != 0b.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 != 0return c;
152         c = modifiers - md.modifiers;
153         if (c != 0return c;
154         c = paramTypes.length - md.paramTypes.length;
155         if (c != 0return c;
156         for (int i = 0; i < paramTypes.length; i++) {
157             c = paramTypes[i].compareTo(md.paramTypes[i]);
158             if (c != 0return 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 }