Package org.uscxml.datamodel.ecmascript

Source Code of org.uscxml.datamodel.ecmascript.ECMAScriptDataModel$NullCallable

package org.uscxml.datamodel.ecmascript;

import java.lang.reflect.Method;
import java.util.Map;

import org.mozilla.javascript.Callable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.FunctionObject;
import org.mozilla.javascript.NativeJSON;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.uscxml.Data;
import org.uscxml.DataModel;
import org.uscxml.Event;
import org.uscxml.Interpreter;
import org.uscxml.NativeIOProcessor;
import org.uscxml.NativeInvoker;
import org.uscxml.StringList;

public class ECMAScriptDataModel extends DataModel {

  public static boolean debug = true;
 
  private class NullCallable implements Callable {
    @Override
    public Object call(Context context, Scriptable scope,
        Scriptable holdable, Object[] objects) {
      return objects[1];
    }
  }

  public Context ctx;
  public Scriptable scope;
  public Interpreter interpreter;

  public Data getScriptableAsData(Object object) {
    Data data = new Data();

    Scriptable s;
    try {
      s = (Scriptable) object;
      String className = s.getClassName(); // ECMA class name
      if (className.toLowerCase().equals("object")) {
        ScriptableObject obj = (ScriptableObject) Context.toObject(s,
            scope);
        for (Object key : obj.getIds()) {
          data.put(Context.toString(key),
              getScriptableAsData(obj.get(key)));
        }
      }
    } catch (ClassCastException e) {
      if (object instanceof Boolean) {
        data.setAtom(Context.toBoolean(object) ? "true" : "false");
        data.setType(Data.Type.INTERPRETED);
      } else if (object instanceof String) {
        data.setAtom((String) object);
        data.setType(Data.Type.VERBATIM);
      } else if (object instanceof Integer) {
        data.setAtom(((Integer) object).toString());
        data.setType(Data.Type.INTERPRETED);
      } else {
        throw new RuntimeException("Unhandled ECMA type "
            + object.getClass().getName());
      }
    }

    return data;
  }

  public ScriptableObject getDataAsScriptable(Data data) {
    throw new UnsupportedOperationException("Not implemented");
  }

  public static boolean jsIn(String stateName) {
    return true;
  }

  @Override
  public DataModel create(Interpreter interpreter) {
    /**
     * Called when an SCXML interpreter wants an instance of this datamodel
     * Be careful to instantiate attributes of instance returned and not
     * *this*
     */
   
    ECMAScriptDataModel newDM = new ECMAScriptDataModel();
    newDM.swigReleaseOwnership();
    newDM.interpreter = interpreter;
    newDM.ctx = Context.enter();

    try {
      newDM.scope = newDM.ctx.initStandardObjects();
    } catch (Exception e) {
      System.err.println(e);
    }

    newDM.scope.put("_name", newDM.scope, interpreter.getName());
    newDM.scope.put("_sessionid", newDM.scope, interpreter.getSessionId());

    // ioProcessors
    {
      Data ioProcs = new Data();
      Map<String, NativeIOProcessor> ioProcNatives = interpreter.getIOProcessors();
      for (String key : ioProcNatives.keySet()) {
        ioProcs.put(key, ioProcNatives.get(key).getDataModelVariables());
      }
      newDM.scope
          .put("_ioprocessors", newDM.scope, new ECMAData(ioProcs));
    }

    // invokers
    {
      Data invokers = new Data();
      Map<String, NativeInvoker> invokersNatives = interpreter.getInvokers();
      for (String key : invokersNatives.keySet()) {
        invokers.put(key, invokersNatives.get(key).getDataModelVariables());
      }
      newDM.scope
          .put("_ioprocessors", newDM.scope, new ECMAData(invokers));
    }
   
    // In predicate (not working as static is required) see:
    // http://stackoverflow.com/questions/3441947/how-do-i-call-a-method-of-a-java-instance-from-javascript/16479685#16479685
    try {
      Class[] parameters = new Class[] { String.class };
      Method inMethod = ECMAScriptDataModel.class.getMethod("jsIn",
          parameters);
      FunctionObject inFunc = new FunctionObject("In", inMethod,
          newDM.scope);
      newDM.scope.put("In", newDM.scope, inFunc);
    } catch (SecurityException e) {
      System.err.println(e);
    } catch (NoSuchMethodException e) {
      System.err.println(e);
    }

    return newDM;
  }

  @Override
  public StringList getNames() {
    /**
     * Register with the following names for the datamodel attribute at the
     * scxml element. <scxml datamodel="one of these">
     */
    StringList ss = new StringList();
    ss.add("ecmascript");
    return ss;
  }

  @Override
  public boolean validate(String location, String schema) {
    /**
     * Validate the datamodel. This make more sense for XML datamodels and
     * is pretty much unused but required as per draft.
     */
    return true;
  }

  @Override
  public void setEvent(Event event) {
    if (debug) {
      System.out.println(interpreter.getName() + " setEvent");
    }
   
    /**
     * Make the current event available as the variable _event in the
     * datamodel.
     */
    ECMAEvent ecmaEvent = new ECMAEvent(event);
    scope.put("_event", scope, ecmaEvent);
  }

  @Override
  public Data getStringAsData(String content) {
    if (debug) {
      System.out.println(interpreter.getName() + " getStringAsData");
    }

    /**
     * Evaluate the string as a value expression and transform it into a
     * JSON-like Data structure
     */
    if (content.length() == 0) {
      return new Data();
    }

    // is it a json expression?
    try {
      Object json = NativeJSON.parse(ctx, scope, content,
          new NullCallable());
      if (json != NativeJSON.NOT_FOUND) {
        return getScriptableAsData(json);
      }
    } catch (org.mozilla.javascript.EcmaError e) {
      System.err.println(e);
    }

    // is it a function call or variable?
    Object x = ctx.evaluateString(scope, content, "uscxml", 0, null);
    if (x == Undefined.instance) {
      // maybe a literal string?
      x = ctx.evaluateString(scope, '"' + content + '"', "uscxml", 0,
          null);
    }
    return getScriptableAsData(x);
  }

  @Override
  public long getLength(String expr) {
    if (debug) {
      System.out.println(interpreter.getName() + " getLength");
    }

    /**
     * Return the length of the expression if it were an array, used by
     * foreach element.
     */

    Object x = scope.get(expr, scope);
    if (x == Undefined.instance) {
      return 0;
    }

    Scriptable result = Context.toObject(x, scope);
    if (result.has("length", result)) {
      return (long) Context.toNumber(result.get("length", result));
    }
    return 0;
  }

  @Override
  public void setForeach(String item, String array, String index,
      long iteration) {
    if (debug) {
      System.out.println(interpreter.getName() + " setForeach");
    }

    /**
     * Prepare an iteration of the foreach element, by setting the variable
     * in index to the current iteration and setting the variable in item to
     * the current item from array.
     */

    try {
      // get the array object
      Scriptable arr = (Scriptable) scope.get(array, scope);

      if (arr.has((int) iteration, arr)) {
        ctx.evaluateString(scope, item + '=' + array + '[' + iteration
            + ']', "uscxml", 1, null);
        if (index.length() > 0) {
          ctx.evaluateString(scope, index + '=' + iteration,
              "uscxml", 1, null);
        }
      } else {
        handleException("");
      }

    } catch (ClassCastException e) {
      System.err.println(e);
    }
  }

  @Override
  public void eval(String scriptElem, String expr) {
    if (debug) {
      System.out.println(interpreter.getName() + " eval");
    }

    /**
     * Evaluate the given expression in the datamodel. This is used foremost
     * with script elements.
     */
    ctx.evaluateString(scope, expr, "uscxml", 1, null);

  }

  @Override
  public String evalAsString(String expr) {
    if (debug) {
      System.out.println(interpreter.getName() + " evalAsString: " + expr);
    }

    /**
     * Evaluate the expression as a string e.g. for the log element.
     */
    if (!ctx.stringIsCompilableUnit(expr)) {
      handleException("");
      return "";
    }
    try {
      Object result = ctx.evaluateString(scope, expr, "uscxml", 1, null);
      return Context.toString(result);
    } catch (IllegalStateException e) {
      System.err.println(e);
      handleException("");
    } catch (EvaluatorException e) {
      System.err.println(e);
      handleException("");
    }
    return "";
  }

  @Override
  public boolean evalAsBool(String elem, String expr) {
    if (debug) {
      System.out.println(interpreter.getName() + " evalAsBool");
    }

    /**
     * Evaluate the expression as a boolean for cond attributes in if and
     * transition elements.
     */
    Object result = ctx.evaluateString(scope, expr, "uscxml", 1, null);
    return Context.toBoolean(result);
  }

  @Override
  public boolean isDeclared(String expr) {
    if (debug) {
      System.out.println(interpreter.getName() + " isDeclared");
    }

    /**
     * The interpreter is supposed to raise an error if we assign to an
     * undeclared variable. This method is used to check whether a location
     * from assign is declared.
     */
    Object x = scope.get(expr, scope);
    return x != Scriptable.NOT_FOUND;
  }

  @Override
  public void init(String dataElem, String location, String content) {
    if (debug) {
      System.out.println(interpreter.getName() + " init");
    }

    /**
     * Called when we pass data elements.
     */
    if (("null").equals(location))
      return;

    if (("null").equals(content) || content.length() == 0) {
      scope.put(location, scope, Context.getUndefinedValue());
      return;
    }

    try {
      Object json = NativeJSON.parse(ctx, scope, content,
          new NullCallable());
      if (json != NativeJSON.NOT_FOUND) {
        scope.put(location, scope, json);
      } else {
        scope.put(location, scope, content);
      }
    } catch (org.mozilla.javascript.EcmaError e) {
      scope.put(location, scope, content);
    }
  }

  @Override
  public void assign(String assignElem, String location, String content) {
    if (debug) {
      System.out.println(interpreter.getName() + " assign");
    }

    /**
     * Called when we evaluate assign elements
     */
    if (("null").equals(location))
      return;

    if (("null").equals(content) || content.length() == 0) {
      scope.put(location, scope, Context.getUndefinedValue());
      return;
    }

    String expr = location + "=" + content;
    ctx.evaluateString(scope, expr, "uscxml", 1, null);
  }

  public void handleException(String cause) {
    Event exceptionEvent = new Event();
    exceptionEvent.setName("error.execution");
    exceptionEvent.setEventType(Event.Type.PLATFORM);

    interpreter.receiveInternal(exceptionEvent);
  }
}
TOP

Related Classes of org.uscxml.datamodel.ecmascript.ECMAScriptDataModel$NullCallable

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.