Package org.renjin.primitives.packaging

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

package org.renjin.primitives.packaging;

import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.io.ByteSource;
import org.renjin.eval.Context;
import org.renjin.eval.EvalException;
import org.renjin.invoke.annotations.SessionScoped;
import org.renjin.sexp.*;

import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
* Session-level registry of namespaces
*
*/
@SessionScoped
public class NamespaceRegistry {

  private static final Symbol BASE = Symbol.get("base");

  /**
   * These packages are part of the R distribution and carry the
   * org.renjin groupId.
   */
  private static final Set<String> CORE_PACKAGES = Sets.newHashSet("datasets", "graphics", "grDevices", "hamcrest",
          "methods", "splines", "stats", "stats4", "utils", "grid");

  private PackageLoader loader;

  /**
   * Maps local names to namespaces
   */
  private Multimap<Symbol, Namespace> localNameMap = HashMultimap.create();
  private Map<FqPackageName, Namespace> namespaceMap = Maps.newHashMap();

  private Map<Environment, Namespace> envirMap = Maps.newIdentityHashMap();
 
  private Context context;
  private final Namespace baseNamespace;

  public NamespaceRegistry(PackageLoader loader, Context context, Environment baseNamespaceEnv) {
    this.loader = loader;
    this.context = context;

    baseNamespace = new BaseNamespace(baseNamespaceEnv);
    localNameMap.put(BASE, baseNamespace);
    envirMap.put(baseNamespaceEnv, baseNamespace);
  }

  public Namespace getBaseNamespace() {
    return baseNamespace;
  }
 
  public Environment getBaseNamespaceEnv() {
    return getBaseNamespace().getNamespaceEnvironment();
  }
 
  public Namespace getNamespace(Environment envir) {
    Namespace ns = envirMap.get(envir);
    if(ns == null) {
      throw new IllegalArgumentException();
    }
    return ns;
  }
 
  public Iterable<Symbol> getLoadedNamespaces() {
    return localNameMap.keySet();
  }

  public Namespace getNamespace(String name) {
    return getNamespace(Symbol.get(name));
  }
 
  public Namespace getNamespace(Symbol symbol) {
    if(symbol.getPrintName().equals("base")) {
      return baseNamespace;

    } else if(couldBeFullyQualified(symbol)) {

      // assume we've been provided with a nice, fully-qualified
      // org.myGroup.myPackageName

      FqPackageName packageName = FqPackageName.fromSymbol(symbol);
      Optional<Namespace> namespace = tryGetNamespace(packageName);
      if(namespace.isPresent()) {
        return namespace.get();
      }

      // this could be a cran package with a dot
      namespace = tryGetNamespace(FqPackageName.cranPackage(symbol));
      if(namespace.isPresent()) {
        return namespace.get();
      }

      throw new EvalException("Could not find package '" + symbol + "'; tried both " +
              packageName + " and " + FqPackageName.cranPackage(symbol));


    } else {

      // we've only got a local package name: use defaults for
      // core package and then fall back to cran namespace

      if(CORE_PACKAGES.contains(symbol.getPrintName())) {
        return getNamespace(FqPackageName.corePackage(symbol));

      } else {
        return getNamespace(FqPackageName.cranPackage(symbol));
      }
    }
  }

  public static Set<String>  getCorePackages()
  {
    return CORE_PACKAGES;
  }

  public Namespace getNamespace(FqPackageName fqPackageName) {
    Optional<Namespace> namespace = tryGetNamespace(fqPackageName);
    if(!namespace.isPresent()) {
      throw new EvalException("Could not load package " + fqPackageName);
    }
    return namespace.get();
  }

  /**
   * Tries to obtain a reference to a namespace using it's fully qualified name,
   * either from among those loaded or those available through the package loader.
   * @param fqName the fully-qualified package name
   * @return the corresponding {@code Namespace}, or {@code null}
   */
  private Optional<Namespace> tryGetNamespace(FqPackageName fqName) {
    if(namespaceMap.containsKey(fqName)) {
      return Optional.of(namespaceMap.get(fqName));
    } else {
      return tryLoad(fqName);
    }
  }

  private Optional<Namespace> tryLoad(FqPackageName fqName) {

    Optional<Package> loadResult = loader.load(fqName);
    if(!loadResult.isPresent()) {
      return Optional.absent();
    } else {
      Package pkg = loadResult.get();
      try {
        // load the serialized functions/values from the classpath
        // and add them to our private namespace environment
        Namespace namespace = createNamespace(pkg);

        // set up the namespace
        populateNamespace(pkg, namespace);

        // setup namespace from NAMESPACE file
        setupImportsExports(pkg, namespace);

        // invoke the .onLoad hook
        if(namespace.getNamespaceEnvironment().hasVariable(Symbol.get(".onLoad"))) {
          StringVector nameArgument = StringVector.valueOf(pkg.getName().getPackageName());
          context.evaluate(FunctionCall.newCall(Symbol.get(".onLoad"), nameArgument, nameArgument),
              namespace.getNamespaceEnvironment());
        }

        return Optional.of(namespace);

      } catch(IOException e) {
        throw new EvalException("IOException while loading package " + fqName, e);
      }
    }
  }

  private boolean couldBeFullyQualified(Symbol name) {
    String string = name.getPrintName();
    return string.indexOf(':') != -1 || string.indexOf('.') != -1;
  }

  /**
   * Populates the namespace from the R-language functions and expressions defined
   * in this namespace.
   *
   */
  private void populateNamespace(Package pkg, Namespace namespace) throws IOException {
    for(NamedValue value : pkg.loadSymbols(context)) {
      namespace.getNamespaceEnvironment().setVariable(Symbol.get(value.getName()), value.getValue());
    }
  }

  /**
   * Sets up imports and exports defined in the NAMESPACE file.
   *
   */
  private void setupImportsExports(Package pkg, Namespace namespace) throws IOException {

    ByteSource namespaceFile = pkg.getResource("NAMESPACE");
    NamespaceDirectiveParser.parse(namespaceFile.asCharSource(Charsets.UTF_8),
        new NamespaceInitHandler(context, this, namespace));
  }


  public boolean isRegistered(Symbol name) {
    return localNameMap.containsKey(name);
  }
 
  public Namespace getBase() {
    return baseNamespace;
  }

  /**
   * Creates a new empty namespace
   */
  public Namespace createNamespace(Package pkg) {
    // each namespace has environment which is the leaf in a hierarchy that
    // looks like this:
    // BASE-NS -> IMPORTS -> ENVIRONMENT

    Environment imports = Environment.createNamedEnvironment(getBaseNamespaceEnv(),
            "imports:" + pkg.getName().toString('.'));

    Environment namespaceEnv = Environment.createNamedEnvironment(imports, pkg.getName().getPackageName());
    Namespace namespace = new Namespace(pkg, namespaceEnv);
    localNameMap.put(pkg.getName().getPackageSymbol(), namespace);
    namespaceMap.put(pkg.getName(), namespace);

    envirMap.put(namespaceEnv, namespace);

    // save the name to the environment
    namespaceEnv.setVariable(".packageName", StringVector.valueOf(pkg.getName().getPackageName()));
    return namespace;
  }

  public boolean isNamespaceEnv(Environment envir) {
    return envirMap.containsKey(envir);
  }
}
TOP

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

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.