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 = (MethodDescriptor) obj;
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 != 0) b.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 != 0) return c;
154 c = modifiers - md.modifiers;
155 if (c != 0) return c;
156 c = paramTypes.length - md.paramTypes.length;
157 if (c != 0) return c;
158 for (int i = 0; i < paramTypes.length; i++) {
159 c = paramTypes[i].compareTo(md.paramTypes[i]);
160 if (c != 0) return 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 }
|