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