Package org.renjin.primitives.packaging

Source Code of org.renjin.primitives.packaging.NamespaceInitHandler

package org.renjin.primitives.packaging;


import com.google.common.collect.Sets;
import org.renjin.eval.Context;
import org.renjin.eval.EvalException;
import org.renjin.invoke.ClassBinding;
import org.renjin.invoke.ClassBindings;
import org.renjin.invoke.reflection.ClassBindingImpl;
import org.renjin.primitives.S3;
import org.renjin.primitives.text.regex.ExtendedRE;
import org.renjin.sexp.*;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;

public class NamespaceInitHandler implements NamespaceDirectiveHandler {

  private Context context;
  private NamespaceRegistry registry;
  private Namespace namespace;
  private final Set<Symbol> groups = Sets.newHashSet(Symbol.get("Ops"), Symbol.get("Math"), Symbol.get("Summary"));

  public NamespaceInitHandler(Context context, NamespaceRegistry registry, Namespace namespace) {
    this.context = context;
    this.registry = registry;
    this.namespace = namespace;
  }

  @Override
  public void export(List<Symbol> symbols) {
    for(Symbol symbol : symbols) {
      namespace.addExport(symbol);
    }
  }

  @Override
  public void exportPattern(String pattern) {
    ExtendedRE re = new ExtendedRE(pattern);
    for(Symbol symbol : namespace.getNamespaceEnvironment().getSymbolNames()) {
      if(re.match(symbol.getPrintName())) {
        namespace.addExport(symbol);
      }
    }
  }

  @Override
  public void import_(List<String> packageNames) {
    for(String packageName : packageNames) {
      Namespace importedNamespace = registry.getNamespace(packageName);
      importedNamespace.copyExportsTo(namespace.getImportsEnvironment());
    }
  }

  @Override
  public void S3method(Symbol genericName, String className) {
    String methodName = genericName.getPrintName() + "." + className;
    S3method(genericName, className, Symbol.get(methodName));
  }

  @Override
  public void importFrom(String packageName, List<Symbol> symbols) {
    Namespace importedNamespace = registry.getNamespace(packageName);
    for(Symbol symbol : symbols) {
      SEXP imported = importedNamespace.getExport(symbol);
      namespace.getNamespaceEnvironment().setVariable(symbol, imported);
    }
  }

  @Override
  public void importFromClass(String className, List<Symbol> methods) {
    Class clazz;
    try {
      clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
      throw new EvalException("Can't find class '" + className + "'");
    }
    ClassBindingImpl classBinding = ClassBindingImpl.get(clazz);

    for(Symbol method : methods) {
      namespace.getNamespaceEnvironment().setVariable(method, classBinding.getStaticMember(method));
    }
  }

  @Override
  public void importClass(List<String> classNames) {
    for(String className : classNames) {
      try {
        Class clazz = Class.forName(className);
        namespace.getNamespaceEnvironment().setVariable(clazz.getSimpleName(),
                new ExternalPtr(clazz));
      } catch(Exception e) {
        System.err.println("Could not load class " + className);
      }  
    }
  }

  @Override
  public void S3method(Symbol genericName, String className, Symbol methodName) {

    Function method = resolveS3Method(methodName);

    Environment definitionEnv = resolveS3DefinitionEnvironment(genericName);
    if(definitionEnv == null || definitionEnv == Environment.EMPTY) {
      throw new EvalException("Could not find definition environment for " + genericName);
    }
    if (!definitionEnv.hasVariable(S3.METHODS_TABLE)) {
      definitionEnv.setVariable(S3.METHODS_TABLE, Environment.createChildEnvironment(context.getBaseEnvironment()));
    }
    Environment methodsTable = (Environment) definitionEnv.getVariable(S3.METHODS_TABLE);
    methodsTable.setVariable(methodName, method);
  }

  private Environment resolveS3DefinitionEnvironment(Symbol genericName) {
    Environment definitionEnv;
    if (groups.contains(genericName)) {
      definitionEnv = registry.getBaseNamespaceEnv();
    } else {
      SEXP genericFunction = namespace.getNamespaceEnvironment().findFunction(context, genericName);
      if (genericFunction == null) {
        System.err.println("Cannot find S3 method definition '" + genericName + "'");
        for (Symbol sym : namespace.getNamespaceEnvironment().getParent().getSymbolNames()) {
          System.err.println("imported: " + sym);
        }
        throw new EvalException("Cannot find S3 method definition '" + genericName + "'");
      }
      definitionEnv = getDefinitionEnv(genericFunction);
    }
    return definitionEnv;
  }

  private Function resolveS3Method(Symbol methodName) {
    SEXP methodExp = namespace.getNamespaceEnvironment().getVariable(methodName).force(context);
    if (methodExp == Symbol.UNBOUND_VALUE) {
      throw new EvalException("Missing export: " + methodName + " not found in " + namespace.getName());
    }
    if (!(methodExp instanceof Function)) {
      throw new IllegalStateException(methodName + ": expected function but was " + methodExp.getClass().getName());
    }
    return (Function) methodExp;
  }

  private Environment getDefinitionEnv(SEXP genericFunction) {
    if(genericFunction instanceof Closure) {
      return ((Closure) genericFunction).getEnclosingEnvironment();
    } else if(genericFunction instanceof PrimitiveFunction) {
      return registry.getBaseNamespaceEnv();
    } else {
      throw new IllegalArgumentException(genericFunction.getClass().getName());
    }
  }

  @Override
  public void useDynlib(String libraryName, List<DynlibEntry> entries, boolean register, String fixes) {
    try {
      Class clazz = namespace.getPackage().getClass(libraryName);

      if(entries.isEmpty()) {
        // add all methods from class file
        for(Method method : clazz.getMethods()) {
          if(isPublicStatic(method)) {
            addGnurMethod(fixes + method.getName(), method);
          }
        }
      } else {
        for(DynlibEntry entry : entries) {
          Method method = findGnurMethod(clazz, entry.getSymbolName());
          if(entry.getAlias() != null) {
            addGnurMethod(fixes + entry.getAlias(), method);
          } else {
            addGnurMethod(fixes + entry.getSymbolName(), method);
          }
        }
      }
    } catch(Exception e) {
      // Allow the program to continue, there may be some packages whose gnur
      // compilation failed but can still partially function.
      System.err.println("WARNING: Failed to import dynLib entries for " + namespace.getName() + ", expect subsequent failures");
    }
  }

  private void addGnurMethod(String name, Method method) {
    namespace.getImportsEnvironment().setVariable(name,
        new ExternalPtr<Method>(method));
  }

  private boolean isPublicStatic(Method method) {
    return Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers());
  }

  private Method findGnurMethod(Class clazz, String symbolName) {
    for(Method method : clazz.getMethods()) {
      if(method.getName().equals(symbolName) && isPublicStatic(method)) {
        return method;
      }
    }
    throw new RuntimeException("Couldn't find method '" + symbolName + "'");
  }
}
TOP

Related Classes of org.renjin.primitives.packaging.NamespaceInitHandler

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.