Package org.jbpm.pvm.internal.script

Source Code of org.jbpm.pvm.internal.script.XPathScriptEngine$XPathCompiledScript

/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: Redistributions of source code
* must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. Neither the name of the Sun Microsystems nor the names of
* is contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

/*
* XPathScriptEngine.java
* @author A. Sundararajan
*/

package org.jbpm.pvm.internal.script;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionResolver;
import javax.xml.xpath.XPathVariableResolver;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;

public class XPathScriptEngine extends AbstractScriptEngine implements Compilable {

  // my factory, may be null
  private ScriptEngineFactory factory;
  private XPathFactory xpathFactory;

  // special context variables for XPath result type and input
  public static final String XPATH_RESULT_TYPE = "com.sun.script.xpath.resultType";
  public static final String XPATH_INPUT_SRC = "com.sun.script.xpath.inputSource";

  // XML namespace prefixes and URIs.
  public static final String XMLNS_COLON = "xmlns:";
  public static final String XPATH_CONTEXT_PREFIX = "context";
  public static final String XPATH_CONTEXT_URI = "http://www.sun.com/java/jsr223/xpath/context";

  private Document objectData;

  public XPathScriptEngine() {
    xpathFactory = XPathFactory.newInstance();
  }

  // my implementation for CompiledScript
  private class XPathCompiledScript extends CompiledScript {

    private XPathExpression expr;

    XPathCompiledScript(XPathExpression expr) {
      this.expr = expr;
    }

    public ScriptEngine getEngine() {
      return XPathScriptEngine.this;
    }

    public Object eval(ScriptContext ctx) throws ScriptException {
      return evalXPath(expr, ctx);
    }
  }

  public CompiledScript compile(String script) throws ScriptException {
    XPathExpression expr = compileXPath(script, context);
    return new XPathCompiledScript(expr);
  }

  public CompiledScript compile(Reader reader) throws ScriptException {
    return compile(readFully(reader));
  }

  public Object eval(String str, ScriptContext ctx) throws ScriptException {
    XPathExpression expr = compileXPath(str, ctx);
    return evalXPath(expr, ctx);
  }

  public Object eval(Reader reader, ScriptContext ctx) throws ScriptException {
    return eval(readFully(reader), ctx);
  }

  public ScriptEngineFactory getFactory() {
    synchronized (this) {
      if (factory == null) {
        factory = new XPathScriptEngineFactory();
      }
    }
    return factory;
  }

  public Bindings createBindings() {
    return new SimpleBindings();
  }

  void setFactory(ScriptEngineFactory factory) {
    this.factory = factory;
  }

  // Internals only below this point

  // find a variable of given qname in given context
  private static Object findVariable(QName qname, ScriptContext ctx) {
    String name;
    int scope;

    if (XPATH_CONTEXT_URI.equals(qname.getNamespaceURI())) {
      name = qname.getLocalPart();
      synchronized (ctx) {
        scope = ctx.getAttributesScope(name);
        if (scope != -1) {
          return ctx.getAttribute(name, scope);
        } // else fallthru
      }
    }
    if (qname.getPrefix() == null || "".equals(qname.getPrefix())) {
      name = qname.getLocalPart();
    } else {
      name = qname.getPrefix() + ":" + qname.getLocalPart();
    }
    synchronized (ctx) {
      scope = ctx.getAttributesScope(name);
      if (scope != -1) {
        return ctx.getAttribute(name, scope);
      } // else fallthru
    }
    return null;
  }

  private static void collectNamespaces(Map<String, String> map, Bindings scope) {
    for (String key : scope.keySet()) {
      if (key.startsWith(XMLNS_COLON)) {
        Object uri = scope.get(key);
        // collect all variables starting with "xmlns:" and
        // collect the prefix to URI mappings.
        String prefix = key.substring(XMLNS_COLON.length());
        if (uri instanceof String) {
          String tmp = (String) uri;
          if (tmp.length() != 0) {
            map.put(prefix, tmp);
          }
        }
      }
    }
  }

  private static NamespaceContext makeNamespaceContext(ScriptContext ctx) {
    // namespace prefix-to-URI mappings
    final Map<String, String> namespaces = new HashMap<String, String>();
    for (int scope : ctx.getScopes()) {
      Bindings bind = ctx.getBindings(scope);
      if (bind != null) {
        // TODO: See what needs to be done....
        // collectNamespaces(namespaces, bind);
      }
    }

    // look for mapping for default XML namespace
    Object def = ctx.getAttribute(XMLConstants.XMLNS_ATTRIBUTE);
    if (def instanceof String) {
      namespaces.put(XMLConstants.DEFAULT_NS_PREFIX, (String) def);
    }

    // standard required mappings by XPath standard
    namespaces.put(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
    namespaces.put(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI);

    // add prefix mapping for XPATH_CONTEXT_PREFIX
    namespaces.put(XPATH_CONTEXT_PREFIX, XPATH_CONTEXT_URI);

    return new NamespaceContext() {

      public String getNamespaceURI(String prefix) {
        if (prefix == null) {
          throw new IllegalArgumentException();
        }
        String uri = namespaces.get(prefix);
        if (uri == null) {
          return XMLConstants.NULL_NS_URI;
        } else {
          return uri;
        }
      }

      public String getPrefix(String namespaceURI) {
        if (namespaceURI == null) {
          throw new IllegalArgumentException();
        }
        for (String prefix : namespaces.keySet()) {
          String uri = namespaces.get(prefix);
          if (namespaceURI.equals(uri)) {
            return prefix;
          }
        }
        return null;
      }

      public Iterator getPrefixes(String namespaceURI) {
        if (namespaceURI == null) {
          throw new IllegalArgumentException();
        }
        List list = new ArrayList();
        for (String prefix : namespaces.keySet()) {
          String uri = namespaces.get(prefix);
          if (namespaceURI.equals(uri)) {
            list.add(prefix);
          }
        }
        return Collections.unmodifiableList(list).iterator();
      }
    };
  }

  private static XPathFunction makeXPathFunction(final Constructor ctr, int arity) {
    if (ctr.getParameterTypes().length != arity) {
      return null;
    }
    return new XPathFunction() {

      public Object evaluate(List args) {
        try {
          return ctr.newInstance(args.toArray());
        } catch (Exception exp) {
          throw new RuntimeException(exp);
        }
      }
    };
  }

  private static XPathFunction makeXPathFunction(final Method method, int arity) {
    int modifiers = method.getModifiers();
    int numArgs = method.getParameterTypes().length;
    if (Modifier.isStatic(modifiers) && numArgs == arity) {
      // static method. expose "as is".
      return new XPathFunction() {

        public Object evaluate(List args) {
          try {
            return method.invoke(null, args.toArray());
          } catch (Exception exp) {
            throw new RuntimeException(exp);
          }
        }
      };
    } else if ((numArgs + 1) == arity) {
      // instance method. treat the first arg as 'this'
      return new XPathFunction() {

        public Object evaluate(List args) {
          List tmp = args.subList(1, args.size());
          try {
            return method.invoke(args.get(0), tmp.toArray());
          } catch (Exception exp) {
            throw new RuntimeException(exp);
          }
        }
      };
    } else {
      return null;
    }
  }

  private static XPathFunction makeXPathFunction(final String funcName, final Invocable invocable) {
    return new XPathFunction() {

      public Object evaluate(List args) {
        try {
          return invocable.invokeFunction(funcName, args.toArray());
        } catch (Exception exp) {
          throw new RuntimeException(exp);
        }
      }
    };
  }

  // make a XPathFunction from given object
  private static XPathFunction makeXPathFunction(QName qname, Object obj, int arity) {
    if (obj == null) {
      return null;
    } else if (obj instanceof XPathFunction) {
      // already XPathFunction - just return
      return (XPathFunction) obj;
    } else if (obj instanceof Method) {
      // a Method object. wrap as XPathFunction
      return makeXPathFunction((Method) obj, arity);
    } else if (obj instanceof Constructor) {
      // a Constructor object. wrap as XPathFunction
      return makeXPathFunction((Constructor) obj, arity);
    } else if (obj instanceof Invocable) {
      // wrap a script function as XPathFunction. Using this,
      // scripts from other languages (for eg. JavaScript) can use
      // this engine and pass script functions as XPathFunction extensions

      return makeXPathFunction(qname.getLocalPart(), (Invocable) obj);
    } else {
      // can't map the object as XPathFunction.
      return null;
    }
  }

  // Internals only below this point
  private XPathExpression compileXPath(String str, final ScriptContext ctx) throws ScriptException {
    // JSR-223 requirement
    ctx.setAttribute("context", ctx, ScriptContext.ENGINE_SCOPE);
    try {
      XPath xpath = xpathFactory.newXPath();
      xpath.setXPathVariableResolver(new XPathVariableResolver() {

        public Object resolveVariable(QName qname) {
          return findVariable(qname, ctx);
        }
      });

      xpath.setXPathFunctionResolver(new XPathFunctionResolver() {

        public XPathFunction resolveFunction(QName qname, int arity) {
          Object obj = findVariable(qname, ctx);
          return makeXPathFunction(qname, obj, arity);
        }
      });
      xpath.setNamespaceContext(makeNamespaceContext(ctx));
      // xpath.setNamespaceContext(new BpmnFunctionResolver());
      // xpath.setXPathFunctionResolver(new BpmnFunctionResolver());
      int begin = str.indexOf("getObjectData") > -1 ? 14 : 0;
      if (begin > 0) {
        String objectDataRef = str.substring(begin + 1, str.indexOf(")") - 1);

        objectData = (Document) ctx.getAttribute(objectDataRef);
        // ctx.setAttribute(XPATH_INPUT_SRC, objectData , 100);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        Source xmlSource = new DOMSource(objectData);
        Result outputTarget = new StreamResult(outputStream);
        TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
        InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
        ctx.setReader(new InputStreamReader(is));

        str = str.substring(str.indexOf(")") + 1);
      }
      XPathExpression xpe = xpath.compile(str);
      return xpe;
    } catch (Exception exp) {
      throw new ScriptException(exp);
    }
  }

  private Object getVariable(ScriptContext ctx, String name) {
    synchronized (ctx) {
      int scope = ctx.getAttributesScope(name);
      if (scope != -1) {
        return ctx.getAttribute(name, scope);
      }
    }
    return null;
  }
  private Object evalXPath(XPathExpression expr, final ScriptContext ctx) throws ScriptException {

    try {
      Object resultType = getVariable(ctx, XPATH_RESULT_TYPE);
      Object input = getVariable(ctx, XPATH_INPUT_SRC);

      InputSource src;
      if (input == null) {
        // no input specified, use context reader
        src = new InputSource(ctx.getReader());
      } else {
        // explicit InputSource specified
        if (input instanceof InputSource) {
          src = (InputSource) input;
        } else if (input instanceof String) {
          src = new InputSource((String) input);
        } else if (input instanceof Reader) {
          src = new InputSource((Reader) input);
        } else if (input instanceof InputStream) {
          src = new InputSource((InputStream) input);
        } else {
          // some other object input type specified (Node/NodeList)
          src = null;
        }
      }

      resultType = XPathConstants.BOOLEAN;

      if (resultType instanceof QName) {
        return (src != null) ? expr.evaluate(src, (QName) resultType) : expr.evaluate(input, (QName) resultType);
      } else {
        return (src != null) ? expr.evaluate(src) : expr.evaluate(input);
      }
    } catch (Exception exp) {
      throw new ScriptException(exp);
    }
  }

  private String readFully(Reader reader) throws ScriptException {
    char[] arr = new char[8 * 1024]; // 8K at a time
    StringBuilder buf = new StringBuilder();
    int numChars;
    try {
      while ((numChars = reader.read(arr, 0, arr.length)) > 0) {
        buf.append(arr, 0, numChars);
      }
    } catch (IOException exp) {
      throw new ScriptException(exp);
    }
    return buf.toString();
  }
}
TOP

Related Classes of org.jbpm.pvm.internal.script.XPathScriptEngine$XPathCompiledScript

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.