MethodDescriptor.java
001 /*
002  * Copyright 2008-2014 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 
023 import static griffon.util.GriffonNameUtils.requireNonBlank;
024 import static java.util.Objects.requireNonNull;
025 
026 /**
027  @author Andres Almiray
028  */
029 public class MethodDescriptor implements Comparable<MethodDescriptor> {
030     private final String methodName;
031     private final String[] paramTypes;
032     private final int hashCode;
033     private final int modifiers;
034 
035     private static final String[] EMPTY_CLASS_PARAMETERS = new String[0];
036 
037     @Nonnull
038     public static MethodDescriptor forMethod(@Nonnull Method method) {
039         return forMethod(method, false);
040     }
041 
042     @Nonnull
043     public static MethodDescriptor forMethod(@Nonnull Method method, boolean removeAbstractModifier) {
044         requireNonNull(method, "Argument 'method' must not be null");
045         int modifiers = method.getModifiers();
046         if (removeAbstractModifier) {
047             modifiers -= Modifier.ABSTRACT;
048         }
049         return new MethodDescriptor(method.getName(), method.getParameterTypes(), modifiers);
050     }
051 
052     private static boolean areParametersCompatible(String[] params1, String[] params2) {
053         if (params1.length != params2.length) {
054             return false;
055         }
056 
057         for (int i = 0; i < params1.length; i++) {
058             String p1 = params1[i];
059             String p2 = params2[i];
060             if (!p1.equals(p2&& !GriffonClassUtils.isMatchBetweenPrimitiveAndWrapperTypes(p1, p2)) {
061                 return false;
062             }
063         }
064 
065         return true;
066     }
067 
068     public MethodDescriptor(@Nonnull String methodName) {
069         this(methodName, EMPTY_CLASS_PARAMETERS, Modifier.PUBLIC);
070     }
071 
072     public MethodDescriptor(@Nonnull String methodName, int modifiers) {
073         this(methodName, EMPTY_CLASS_PARAMETERS, modifiers);
074     }
075 
076     public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes) {
077         this(methodName, paramTypes, Modifier.PUBLIC);
078     }
079 
080     public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes) {
081         this(methodName, paramTypes, Modifier.PUBLIC);
082     }
083 
084     public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes, int modifiers) {
085         this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank");
086         requireNonNull(paramTypes, "Argument 'paramTypes' must not be null");
087 
088         this.paramTypes = new String[paramTypes.length];
089         for (int i = 0; i < paramTypes.length; i++) {
090             this.paramTypes[i= paramTypes[i].getName();
091         }
092 
093         this.modifiers = modifiers;
094         this.hashCode = 31 * methodName.hashCode() + modifiers;
095     }
096 
097     public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes, int modifiers) {
098         this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank");
099         requireNonNull(paramTypes, "Argument 'paramTypes' must not be null");
100 
101         this.paramTypes = new String[paramTypes.length];
102         System.arraycopy(paramTypes, 0this.paramTypes, 0, 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 }