package org.netbeans.gradle.project.others; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import org.jtrim.utils.ExceptionHelper; public final class PluginClassMethod { private static final Logger LOGGER = Logger.getLogger(PluginClassMethod.class.getName()); private static final Class<?>[] EMPTY_CLASS_ARR = new Class<?>[0]; private final ClassFinder pluginClass; private final String methodName; private final ClassFinder[] argTypeFinders; private final AtomicReference<Method> methodRef; @SuppressWarnings("VolatileArrayField") private volatile Class<?>[] argTypesCache; public PluginClassMethod(ClassFinder pluginClass, String methodName, Class<?>... argTypes) { this(pluginClass, methodName, ReflectionHelper.constClassFinders(argTypes)); } public PluginClassMethod(ClassFinder pluginClass, String methodName, ClassFinder... argTypeFinders) { ExceptionHelper.checkNotNullArgument(pluginClass, "pluginClass"); ExceptionHelper.checkNotNullArgument(methodName, "methodName"); this.pluginClass = pluginClass; this.methodName = methodName; this.argTypeFinders = argTypeFinders.clone(); this.methodRef = new AtomicReference<>(null); this.argTypesCache = null; ExceptionHelper.checkNotNullElements(this.argTypeFinders, "argTypeFinders"); } public static PluginClassMethod noArgMethod(ClassFinder pluginClass, String methodName) { return new PluginClassMethod(pluginClass, methodName, EMPTY_CLASS_ARR); } private Class<?>[] findArgTypes() { Class<?>[] result = new Class<?>[argTypeFinders.length]; for (int i = 0; i < result.length; i++) { Class<?> argType = argTypeFinders[i].tryGetClass(); if (argType == null) { return null; } result[i] = argTypeFinders[i].tryGetClass(); } return result; } private Class<?>[] getArgTypes() { Class<?>[] result = argTypesCache; if (result == null) { result = findArgTypes(); if (result != null) { argTypesCache = result; } } return result; } public Method tryGetMethod() { Method result = methodRef.get(); if (result == null) { methodRef.compareAndSet(null, tryFindMethod()); result = methodRef.get(); } return result; } private Method tryFindMethod() { Class<?> type = pluginClass.tryGetClass(); if (type == null) { return null; } try { return type.getMethod(methodName, getArgTypes()); } catch (NoSuchMethodException ex) { return null; } } private String getClassName() { Class<?> type = pluginClass.tryGetClass(); return type != null ? type.getName() : "?"; } public Object tryInvoke(Object instance, Object... arguments) { Method method = tryGetMethod(); try { if (method != null) { return method.invoke(instance, arguments); } else { LOGGER.log(Level.WARNING, "Missing method: {0} for class {1}", new Object[]{methodName, getClassName()}); return null; } } catch (IllegalAccessException | InvocationTargetException ex) { throw new RuntimeException(ex); } } }