Package com.cueup.hegemon

Source Code of com.cueup.hegemon.Script

/*
* Copyright 2012 the hegemon 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 com.cueup.hegemon;

import com.cueup.hegemon.annotations.ReferencedByJavascript;
import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeJavaClass;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Wrapper;

import java.util.List;
import java.util.Set;

/**
* A pre-parsed script.
*/
public class Script {

  /**
   * Logging.
   */
  private static final Log LOG = LogFactory.getLog(Script.class);

  /**
   * Allows for user defined script location.
   */
  private final LoadPath loadPath;

  /**
   * The 'global' scope for this script.
   * Scripts are evaluated in this context.
   */
  private final Scriptable sharedScope;

  /**
   * Whether a script has already been loaded into this context.
   */
  private final Set<String> loaded;

  /**
   * Where we keep values that need to exist cross script invocations.
   */
  @ReferencedByJavascript
  public static final Cache<ValueName, Object> STATIC_VALUES =
      CacheBuilder.newBuilder().build();


  // TODO(kevinclark): lambda l: try: l(enterContext()) finally: exitContext()
  // Use these wrappers instead of Context.enter / Context.exit
  // to ensure correct version is used.


  /**
   * Enter a new lexical context.
   * @return the context object.
   */
  public static Context enterContext() {
    final Context context = Context.enter();
    context.setLanguageVersion(Context.VERSION_1_8);
    return context;
  }


  /**
   * Exit the current context.
   */
  public static void exitContext() {
    Context.exit();
  }


  /**
   * Load a new script context from a source, found with a locator,
   * loading globalFiles.
   * @param source - The source code to be run.
   * @param loadPath - How to find any files loaded.
   * @param globalFiles - Files to load to run this source.
   * @throws LoadError when files don't load properly.
   */
  public Script(final String source, final LoadPath loadPath,
                final String... globalFiles) throws LoadError {
    this.loadPath = loadPath;
    this.loaded = Sets.newHashSet();

    Context context = enterContext();
    try {
      this.sharedScope = context.initStandardObjects();
      ScriptableObject.putProperty(this.sharedScope, "log",
          Context.javaToJS(LOG, this.sharedScope));
      ScriptableObject.putProperty(this.sharedScope, "hegemon",
          Context.javaToJS(this, this.sharedScope));

      for (String globalFile : globalFiles) {
        load(globalFile);
      }

      context.evaluateString(this.sharedScope, source, "main", 1, null);
    } finally {
      exitContext();
    }
  }


  /**
   * Load the script located with the Script's loadPath with the given filename.
   * @param scriptName - the name of the script to load (sans .js).
   * @throws LoadError when unable to load the associated resource.
   */
  public void load(final String scriptName) throws LoadError {
    if (this.loaded.contains(scriptName)) {
      return;
    }
    this.loaded.add(scriptName);

    String filename = scriptName + ".js";
    Context context = enterContext();
    try {
      String code = this.loadPath.load(filename);
      context.evaluateString(this.sharedScope, code, filename, 1, null);
    } finally {
      exitContext();
    }
  }


  /**
   * Returns the source in the given filename.
   * @param filename the source to load.
   * @return the text in the source file.
   * @throws LoadError when unable to load the associated resource.
   */
  public String read(final String filename) throws LoadError {
    return this.loadPath.load(filename);
  }


  /**
   * Run the given function by name in the current context.
   * @param functionName - the name of the function to run.
   * @param values - the arguments passed to the function.
   * @return the result of the function call.
   */
  public Object run(final String functionName, final Object... values) {
    // Create a local copy of the bindings so we can multi-thread.
    Context context = enterContext();

    try {
      final Scriptable localScope = context.newObject(this.sharedScope);
      localScope.setPrototype(this.sharedScope);
      localScope.setParentScope(null);

      List<String> names = Lists.newArrayList();
      for (int i = 0; i < values.length; i++) {
        final Object value = values[i];
        if (value instanceof String || value instanceof Number
            || value instanceof Boolean || value instanceof Scriptable) {
          ScriptableObject.putProperty(localScope, "__p" + i, values[i]);
        } else {
          ScriptableObject.putProperty(localScope, "__p" + i,
              Context.javaToJS(values[i], localScope));
        }
        names.add("__p" + i);
      }
      String code = functionName + "(" + Joiner.on(",").join(names) + ");";
      return unwrap(context.evaluateString(localScope, code, "main", 1, null));
    } finally {
      exitContext();
    }
  }

  /**
   * Unwrap the object return from the js runtime.
   *
   * Cribbed from com.sun.phobos.script.javascript.ExternalScriptable.java
   * BSD licensed
   *
   * @param jsObj the object to unwrap.
   * @return the unwrapped object.
   */
  private Object unwrap(final Object jsObj) {
    if (jsObj instanceof Wrapper) {
      Wrapper njb = (Wrapper) jsObj;

      if (njb instanceof NativeJavaClass) {
        return njb;
      }

      Object obj = njb.unwrap();
      if (obj instanceof Number || obj instanceof String
          || obj instanceof Boolean || obj instanceof Character) {
        // special type wrapped -- we just leave it as is.
        return njb;
      } else {
        // return unwrapped object for any other object.
        return obj;
      }
    } else {
      return jsObj;
    }
  }
}
TOP

Related Classes of com.cueup.hegemon.Script

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.