/* * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package griffon.util; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import static griffon.util.GriffonNameUtils.requireNonBlank; import static java.util.Objects.requireNonNull; /** * @author Andres Almiray */ public class MethodDescriptor implements Comparable<MethodDescriptor> { private final String methodName; private final String[] paramTypes; private final int hashCode; private final int modifiers; private static final String[] EMPTY_CLASS_PARAMETERS = new String[0]; @Nonnull public static MethodDescriptor forMethod(@Nonnull Method method) { return forMethod(method, false); } @Nonnull public static MethodDescriptor forMethod(@Nonnull Method method, boolean removeAbstractModifier) { requireNonNull(method, "Argument 'method' must not be null"); int modifiers = method.getModifiers(); if (removeAbstractModifier) { modifiers -= Modifier.ABSTRACT; } return new MethodDescriptor(method.getName(), method.getParameterTypes(), modifiers); } private static boolean areParametersCompatible(String[] params1, String[] params2) { if (params1.length != params2.length) { return false; } for (int i = 0; i < params1.length; i++) { String p1 = params1[i]; String p2 = params2[i]; if (!p1.equals(p2) && !GriffonClassUtils.isMatchBetweenPrimitiveAndWrapperTypes(p1, p2)) { return false; } } return true; } public MethodDescriptor(@Nonnull String methodName) { this(methodName, EMPTY_CLASS_PARAMETERS, Modifier.PUBLIC); } public MethodDescriptor(@Nonnull String methodName, int modifiers) { this(methodName, EMPTY_CLASS_PARAMETERS, modifiers); } public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes) { this(methodName, paramTypes, Modifier.PUBLIC); } public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes) { this(methodName, paramTypes, Modifier.PUBLIC); } public MethodDescriptor(@Nonnull String methodName, @Nonnull Class<?>[] paramTypes, int modifiers) { this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank"); requireNonNull(paramTypes, "Argument 'paramTypes' must not be null"); this.paramTypes = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { this.paramTypes[i] = paramTypes[i].getName(); } this.modifiers = modifiers; this.hashCode = 31 * methodName.hashCode() + modifiers; } public MethodDescriptor(@Nonnull String methodName, @Nonnull String[] paramTypes, int modifiers) { this.methodName = requireNonBlank(methodName, "Argment 'methodName' must not be blank"); requireNonNull(paramTypes, "Argument 'paramTypes' must not be null"); this.paramTypes = Arrays.copyOf(paramTypes, paramTypes.length); this.modifiers = modifiers; this.hashCode = 31 * methodName.hashCode() + modifiers; } @Nonnull public String getName() { return methodName; } @Nonnull public String[] getParameterTypes() { return paramTypes; } public int getModifiers() { return modifiers; } public boolean equals(Object obj) { if (!(obj instanceof MethodDescriptor)) { return false; } MethodDescriptor md = (MethodDescriptor) obj; return methodName.equals(md.methodName) && modifiers == md.modifiers && areParametersCompatible(paramTypes, md.paramTypes); } public int hashCode() { return hashCode; } public String toString() { StringBuilder b = new StringBuilder(); b.append(Modifier.toString(modifiers)).append(" "); b.append(methodName).append("("); for (int i = 0; i < paramTypes.length; i++) { if (i != 0) b.append(", "); b.append(paramTypes[i]); } b.append(")"); return b.toString(); } public int compareTo(MethodDescriptor md) { int c = methodName.compareTo(md.methodName); if (c != 0) return c; c = modifiers - md.modifiers; if (c != 0) return c; c = paramTypes.length - md.paramTypes.length; if (c != 0) return c; for (int i = 0; i < paramTypes.length; i++) { c = paramTypes[i].compareTo(md.paramTypes[i]); if (c != 0) return c; } return 0; } public boolean matches(@Nonnull MethodDescriptor md) { requireNonNull(md, "Argument 'methodDescriptor' must not be null"); if (!methodName.equals(md.methodName) || modifiers != md.modifiers || paramTypes.length != md.paramTypes.length) { return false; } for (int i = 0; i < paramTypes.length; i++) { Class<?> param1 = loadClass(paramTypes[i]); Class<?> param2 = loadClass(md.paramTypes[i]); if (param1 != null && param2 != null && !GriffonClassUtils.isAssignableOrConvertibleFrom(param1, param2)) { return false; } } return true; } @Nullable private Class<?> loadClass(@Nonnull String classname) { try { return Class.forName(classname, true, getClass().getClassLoader()); } catch (ClassNotFoundException e) { if (GriffonClassUtils.PRIMITIVE_TYPE_COMPATIBLE_TYPES.containsKey(classname)) { try { classname = GriffonClassUtils.PRIMITIVE_TYPE_COMPATIBLE_TYPES.get(classname); return Class.forName(classname, true, getClass().getClassLoader()); } catch (ClassNotFoundException e1) { return null; } } else { return null; } } } }