Package openperipheral.adapter

Source Code of openperipheral.adapter.AdapterWrapper$MethodExecutorFactory

package openperipheral.adapter;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import openmods.Log;
import openmods.utils.ReflectionHelper;
import openperipheral.adapter.method.MethodDeclaration;
import openperipheral.adapter.method.MethodDeclaration.CallWrap;
import openperipheral.api.*;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.*;

public abstract class AdapterWrapper<E extends IMethodExecutor> implements IAdapterMethodsList<E> {

  private static boolean isFreeform(AnnotatedElement element, boolean defaultValue) {
    Freeform freeform = element.getAnnotation(Freeform.class);
    return freeform != null? freeform.value() : defaultValue;
  }

  private static String[] getPrefixes(AnnotatedElement element, String[] defaultValue) {
    if (element == null) return defaultValue; // possible for net.minecraft
                          // classes
    Prefixed prefixed = element.getAnnotation(Prefixed.class);
    return (prefixed != null)? prefixed.value() : defaultValue;
  }

  protected final List<E> methods;
  protected final Class<?> targetCls;
  protected final Class<?> adapterClass;

  protected AdapterWrapper(Class<?> adapterClass, Class<?> targetClass) {
    this.adapterClass = adapterClass;
    this.targetCls = targetClass;
    this.methods = ImmutableList.copyOf(buildMethodList());
  }

  @Override
  public List<E> listMethods() {
    return methods;
  }

  @Override
  public Class<?> getTargetClass() {
    return targetCls;
  }

  @Override
  public String describeType() {
    return "wrapped (source: " + adapterClass.toString() + ")";
  }

  protected abstract List<E> buildMethodList();

  protected void namesFromAnnotation(String[] prefixes, MethodDeclaration decl) {
    int i = 0;
    for (String name : prefixes)
      decl.nameJavaArg(i++, name);
  }

  protected abstract void validateArgTypes(MethodDeclaration decl);

  protected abstract void nameDefaultParameters(MethodDeclaration decl);

  protected interface MethodExecutorFactory<E extends IMethodExecutor> {
    public E createExecutor(Method method, MethodDeclaration decl, Map<String, Method> proxyArgs);
  }

  protected MethodDeclaration createDeclaration(Method method) {
    LuaMethod methodAnn = method.getAnnotation(LuaMethod.class);
    if (methodAnn != null) return new MethodDeclaration(method, methodAnn);

    LuaCallable callableAnn = method.getAnnotation(LuaCallable.class);
    if (callableAnn != null) return new MethodDeclaration(method, callableAnn);

    return null;
  }

  private void addProxyArgs(Map<String, Method> result, String defaultName, Class<?>[] defaultArgs, ProxyArg arg) {
    String name = arg.argName();

    String[] names = arg.methodNames();
    if (names.length == 0) names = new String[] { defaultName };

    Class<?> args[] = arg.args();
    if (args.length == 1 && args[0] == void.class) args = defaultArgs;

    Method proxiedMethod = ReflectionHelper.getMethod(targetCls, names, args);
    Preconditions.checkNotNull(proxiedMethod, "Can find proxy argument '%s' for method %s %s in class (adapter: %s)",
        name, targetCls, Arrays.toString(names), Arrays.toString(args), targetCls, adapterClass);
    proxiedMethod.setAccessible(true);
    Method prev = result.put(name, proxiedMethod);
    Preconditions.checkState(prev == null, "Duplicated proxy arg name '%s' in adapter '%s'", name, adapterClass);
  }

  protected static IMethodProxy createProxy(final Object target, final Method method) {
    return new IMethodProxy() {
      @SuppressWarnings("unchecked")
      @Override
      public <T> T call(Object... args) {
        try {
          return (T)method.invoke(target, args);
        } catch (Throwable t) {
          throw Throwables.propagate(t);
        }
      }
    };
  }

  protected static CallWrap nameAdapterMethods(Object target, Map<String, Method> proxyArgs, CallWrap wrap) {
    for (Map.Entry<String, Method> e : proxyArgs.entrySet())
      wrap.setJavaArg(e.getKey(), createProxy(target, e.getValue()));

    return wrap;
  }

  protected List<E> buildMethodList(boolean defaultIsFreeform, MethodExecutorFactory<E> factory) {
    List<E> result = Lists.newArrayList();
    final boolean clsIsFreeform = isFreeform(adapterClass, defaultIsFreeform);

    final String[] packagePrefixes = getPrefixes(adapterClass.getPackage(), null);
    final String[] classPrefixes = getPrefixes(adapterClass, packagePrefixes);

    Method[] clsMethods;
    try {
      clsMethods = adapterClass.getDeclaredMethods();
    } catch (Throwable t) {
      // Trust no one. We got report about ClassNotFound from this place
      Log.severe(t, "Can't get adapter %s methods (possible sideness fail), bailing out", adapterClass);
      return result;
    }

    for (Method method : clsMethods) {
      MethodDeclaration decl = createDeclaration(method);

      if (decl == null) continue;

      Map<String, Method> allProxyArgs = Maps.newHashMap();

      Class<?>[] luaArgs = decl.getLuaArgTypes();
      final ProxyArg proxyArg = method.getAnnotation(ProxyArg.class);
      if (proxyArg != null) addProxyArgs(allProxyArgs, method.getName(), luaArgs, proxyArg);

      final ProxyArgs proxyArgs = method.getAnnotation(ProxyArgs.class);
      if (proxyArgs != null) for (ProxyArg arg : proxyArgs.value())
        addProxyArgs(allProxyArgs, method.getName(), luaArgs, arg);

      E exec = factory.createExecutor(method, decl, ImmutableMap.copyOf(allProxyArgs));

      if (!isFreeform(method, clsIsFreeform)) {
        final String[] methodPrefixes = getPrefixes(method, classPrefixes);
        if (methodPrefixes != null) namesFromAnnotation(methodPrefixes, decl);
        else nameDefaultParameters(decl);
      }

      validateArgTypes(decl);
      for (String proxyArgName : allProxyArgs.keySet())
        decl.declareJavaArgType(proxyArgName, IMethodProxy.class);

      decl.validate();

      result.add(exec);
    }

    return result;
  }

}
TOP

Related Classes of openperipheral.adapter.AdapterWrapper$MethodExecutorFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.