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 }
|