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, 0, this.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 = (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 }
|