Package client.net.sf.saxon.ce.js

Source Code of client.net.sf.saxon.ce.js.IXSLFunction

package client.net.sf.saxon.ce.js;

import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.dom.HTMLDocumentWrapper;
import client.net.sf.saxon.ce.dom.HTMLDocumentWrapper.DocType;
import client.net.sf.saxon.ce.dom.HTMLNodeWrapper;
import client.net.sf.saxon.ce.dom.XMLDOM;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.SequenceIterator;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.om.ValueRepresentation;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.iter.EmptyIterator;
import client.net.sf.saxon.ce.tree.iter.JsArrayIterator;
import client.net.sf.saxon.ce.tree.iter.SingletonIterator;
import client.net.sf.saxon.ce.type.AnyItemType;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.*;
import client.net.sf.saxon.ce.value.StringValue;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Node;
import com.google.gwt.user.client.Event;

import java.util.logging.Logger;

/**
* This class implements Saxon-CE extension functions designed to allow interoperability between XSLT
* and JavaScript.
*/
public class IXSLFunction extends FunctionCall {

    private String localName;
    private Logger logger = Logger.getLogger("IXSLFunction");
   
    private static int injectCount = 0;

    public IXSLFunction(String localName, Expression[] arguments) {
        this.localName = localName;
        setArguments(arguments);
    }

    @Override
    public StructuredQName getFunctionName() {
        return new StructuredQName(NamespaceConstant.IXSL, "", localName);
    }

    @Override
    protected void checkArguments(ExpressionVisitor visitor) throws XPathException {

    }

    @Override
    public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
        return this;
    }

    @Override
    public ItemType getItemType(TypeHierarchy th) {
        return AnyItemType.getInstance();
    }

    @Override
    protected int computeCardinality() {
        return StaticProperty.ALLOWS_ZERO_OR_MORE;
    }

    @Override
    protected int computeSpecialProperties() {
        return StaticProperty.HAS_SIDE_EFFECTS;
    }

    private static native JavaScriptObject jsWindow()
    /*-{
       return $wnd;
    }-*/;

    /**
     * Call a JavaScript function (a method on a JavaScript object)
     * @param target the JavaScript object owning the method
     * @param member the name of the function to be called as a string
     * @param arguments the arguments to the method call
     * @return the result of evaluating the expression. To ensure that this is always
     * an Object, it is returned as a type/value pair
     */

   private static native JavaScriptObject jsCall(JavaScriptObject target, String member, JavaScriptObject arguments)
    /*-{
       var v = target[member].apply(target, arguments);
       return { type: typeof v, value: v}
    }-*/;

    private static native Object jsProperty(JavaScriptObject target, String member)
    /*-{
       return target[member];
    }-*/;
   
    /**
     * Set a JavaScript property value (a property of a JavaScript object)
     * @param target the JavaScript object owning the method
     * @param member the name of the property to be fetched as a string - may be
     *        a chained set of properties e.g. loction.href
     */
    public static native void setProperty(JavaScriptObject target, String member, Object value)
    /*-{
       var props = member.split('.');
       var newTarget = target;
       for (var count = 0; count < props.length - 1; count++){
          newTarget = newTarget[props[count]];
       }
       newTarget[props[props.length -1]] = value;
      
    }-*/;
  
    /**
     * Get a JavaScript property (a property of a JavaScript object)
     * @param target the JavaScript object owning the method
     * @param member the name of the property to be fetched as a string - may be
     *        a chained set of properties e.g. loction.href
     * @return the result of evaluating the expression. To ensure that this is always
     * an Object, it is returned as a type/value pair
     */
    private static native JavaScriptObject jsSplitPropertyTypeAndValue(JavaScriptObject target, String member)
    /*-{
       var props = member.split('.');
       var v = target;
       for (var count = 0; count < props.length; count++){
          v = v[props[count]];
       }
       return { type: typeof v, value: v}
    }-*/;

    private static native double jsNumberProperty(JavaScriptObject target, String member)
    /*-{
       return target[member];
    }-*/;

    private static native boolean jsBooleanProperty(JavaScriptObject target, String member)
    /*-{
       return target[member];
    }-*/;

    public static native JavaScriptObject jsArray(int length) /*-{
        return new Array(length);
     }-*/;

    public static native void jsSetArrayItem(JavaScriptObject array, int index, Object value) /*-{
        array[index] = value;
     }-*/;
   
    private static native Object jsGetArrayItem(JavaScriptObject array, int index) /*-{
      return array[index];
    }-*/;
   
    private static native int jsGetArrayLength(JavaScriptObject array) /*-{
  return array.length;
    }-*/;
   
    /**
     * Special issues with determining if an array - because other object
     * types such as Window are represented as arrays of length 1
     */
    private static native boolean isJsArray(JavaScriptObject obj) /*-{
       return (obj.length != undefined);
    }-*/;

    @SuppressWarnings("rawtypes")
  public static SequenceIterator convertFromJavaScript(Object jsValue, Configuration config) {
        if (jsValue == null) {
            return EmptyIterator.getInstance();
        } else if (jsValue instanceof String) {
            return SingletonIterator.makeIterator(new StringValue((String)jsValue));
        } else if (jsValue instanceof Double) {
            return SingletonIterator.makeIterator(new DoubleValue((Double)jsValue));
        } else if (jsValue instanceof Boolean) {
            return SingletonIterator.makeIterator(BooleanValue.get((Boolean)jsValue));
        } else if (!(jsValue instanceof JavaScriptObject)) {
          return EmptyIterator.getInstance();
        }
       
      JavaScriptObject jsObj = (JavaScriptObject)jsValue;    
      short nodeType = getNodeType(jsObj);
      if (nodeType == -1) {
          if (isJsArray(jsObj) && jsGetArrayLength(jsObj) > 1) {
            return new JsArrayIterator((JsArray)jsObj, config);
          } else {
            return SingletonIterator.makeIterator(new JSObjectValue(jsObj));
          }
      }

        com.google.gwt.dom.client.Document page = ((Node)jsValue).getOwnerDocument();
        if (page == null ) {
       com.google.gwt.dom.client.Document doc = (Document)jsValue;
       DocType jsDocType = (doc == Document.get())? DocType.UNKNOWN : DocType.NONHTML;
       HTMLDocumentWrapper docWrapper =  new HTMLDocumentWrapper(doc, doc.getURL(), config, jsDocType);
       return SingletonIterator.makeIterator(docWrapper);
        } else {
      DocType jsDocType = (page == Document.get())? DocType.UNKNOWN : DocType.NONHTML;
          HTMLDocumentWrapper htmlDoc = new HTMLDocumentWrapper(page, page.getURL(), config, jsDocType);
          HTMLNodeWrapper htmlNode = htmlDoc.wrap((Node) jsValue);
          return SingletonIterator.makeIterator(htmlNode);
        }         
   }
      
   
    private static native short getNodeType(JavaScriptObject obj) /*-{
          var out = obj.nodeType;
            return (out == null) ? -1 : out;
    }-*/;
 
    public static Object convertToJavaScript(ValueRepresentation val) throws XPathException {
      if (val == null || val instanceof EmptySequence) {
        return null;
      }
        if (val instanceof Item) {
            if (val instanceof StringValue) {
              return ((StringValue)val).getStringValue();
          } else if (val instanceof BooleanValue) {
              return ((BooleanValue)val).getBooleanValue();
          } else if (val instanceof NumericValue) {
              return ((NumericValue)val).getDoubleValue();
          } else if (val instanceof HTMLNodeWrapper) {
              return ((HTMLNodeWrapper)val).getUnderlyingNode();
          } else if (val instanceof JSObjectValue) {
              return ((JSObjectValue)val).getJavaScriptObject();
          } else {
                throw new XPathException("Cannot convert " + val.getClass() + " to Javascript object");
            }
        } else {
            int seqLength = ((Value)val).getLength();
            if (seqLength == 0) {
                return null;
            } else if (seqLength == 1) {
                return convertToJavaScript(((Value)val).asItem());
            } else {
                return convertSequenceToArray(val, seqLength);
            }
        }
    }
   
    private static JavaScriptObject convertSequenceToArray(ValueRepresentation val, int seqLength) throws XPathException {
        JavaScriptObject jsItems = jsArray(seqLength);
        SequenceIterator iterator = Value.getIterator(val);
        int i = 0;
        while (true) {
            Item item = iterator.next();
            if (item == null) {
                break;
            }
            Object jsObject = convertToJavaScript(item);
            jsSetArrayItem(jsItems, i, jsObject);
            i++;
        }
        return jsItems;
    }

    private Object getValueFromTypeValuePair(JavaScriptObject pair) {
        String type = (String)jsProperty(pair, "type");
        if ("number".equals(type)) {
            return new Double(jsNumberProperty(pair, "value"));
        } else if ("boolean".equals(type)) {
            return Boolean.valueOf(jsBooleanProperty(pair, "value"));
        } else {
            return jsProperty(pair, "value");
        }
    }
   
       private SequenceIterator evaluateJsFunction(String script, XPathContext context) throws XPathException {
        injectCount++;
        String fnName = "fnName" + injectCount;
        script = script.trim();
        String fnScript = "function " + fnName + "() { return " + script + "; }";
        JSObjectValue item =  new JSObjectValue(jsWindow());
        JavaScriptObject target = (JavaScriptObject)convertToJavaScript(item);
         
          ScriptInjector.FromString fs = new ScriptInjector.FromString(fnScript);
          fs.setWindow(target);
          fs.inject();
          JavaScriptObject jsArgs = jsArray(0);
          try {
            Object result = getValueFromTypeValuePair(jsCall(target, fnName, jsArgs));
            return convertFromJavaScript(result, context.getConfiguration());
          } catch(JavaScriptException jexc) {
            throw(new XPathException("JavaScriptException: " + jexc.getDescription() +
                "\noccurred on evaluating:\n" + script));
          }
    }
      
    public SequenceIterator iterate(XPathContext context) throws XPathException {
      try {
        if (localName.equals("window")) {
            Item item = new JSObjectValue(jsWindow());
            return SingletonIterator.makeIterator(item);

        } else if (localName.equals("eval")) {
          String script = argument[0].evaluateAsString(context).toString();
          return evaluateJsFunction(script, context);
         
        } else if (localName.equals("call")) {
          ValueRepresentation itemVal = (ValueRepresentation)argument[0].evaluateItem(context);
          JavaScriptObject target = (JavaScriptObject)convertToJavaScript(itemVal);
          if (target != null) {
              String method = argument[1].evaluateAsString(context).toString();
              JavaScriptObject jsArgs = jsArray(argument.length - 2);
              for (int i=2; i<argument.length; i++) {
                  ValueRepresentation val = SequenceExtent.makeSequenceExtent(argument[i].iterate(context));
                  jsSetArrayItem(jsArgs, i-2, convertToJavaScript(val));
              }
              // Issue: if following throws an exception - GWT doesn't always allow it to be caught - it's rethrown
              // as a GWT unhandled exception
              try {
                JavaScriptObject jsObj = jsCall(target, method, jsArgs);
                Object result = getValueFromTypeValuePair(jsObj);
                return convertFromJavaScript(result, context.getConfiguration());
              } catch(Exception e) {
                boolean doRetry = false;
               
                // try again setting any null values to zero-length arrays
                for (int i = 0; i < argument.length - 2; i++) {
                  if (jsGetArrayItem(jsArgs, i) == null) {
                    jsSetArrayItem(jsArgs, i, jsArray(0));
                    doRetry = true;
                  }
                }
                if (doRetry) {
                    try {
                      Object result = getValueFromTypeValuePair(jsCall(target, method, jsArgs));
                      return convertFromJavaScript(result, context.getConfiguration());
                    } catch(Exception e2) {}                 
                }
                // if we get this far then throw exception - recovery failed
                throw(new XPathException("JavaScriptException in ixsl:call(): Object does not support property or method '" +
                    method + "' with " + (argument.length - 2) + " argument(s)."));
              }
          } else {
              throw(new XPathException("JavaScriptException in ixsl:call(): Call target object is null or undefined"));
          }
        } else if (localName.equals("get")) {
          ValueRepresentation itemVal = (ValueRepresentation)argument[0].evaluateItem(context);
          JavaScriptObject target = (JavaScriptObject)convertToJavaScript(itemVal);
          if (target != null) {
                String property = argument[1].evaluateAsString(context).toString();
                Object result;
                try {
                  result = getValueFromTypeValuePair(jsSplitPropertyTypeAndValue(target, property));
                } catch(Exception e) {
                  throw(new XPathException("JavaScriptException in ixsl:get() for property: " + property));
                }
                return convertFromJavaScript(result, context.getConfiguration());           
          } else {
              throw(new XPathException("JavaScriptException in ixsl:get(): Get target object is null or undefined"));
          }
        } else if (localName.equals("page")) {
            return SingletonIterator.makeIterator(context.getConfiguration().getHostPage());
        } else if (localName.equals("source")) {
            return SingletonIterator.makeIterator(context.getController().getSourceNode());
        } else if (localName.equals("event")) {
            Event event = (Event)context.getController().getUserData("Saxon-CE", "current-event");
            return SingletonIterator.makeIterator(new JSObjectValue(event));
        } else if (localName.equals("parse-xml")) {
          String data = argument[0].evaluateAsString(context).toString();
          return convertFromJavaScript(XMLDOM.parseXML(data), context.getConfiguration());
        }else {
          // previously there was no warning - strictly - this should be caught at compile time
          logger.warning("No such IXSL function: '" + localName + "' - empty sequence returned");
          return EmptyIterator.getInstance();
        } // end of else
      } // end of try block
      catch(XPathException e) {
            e.maybeSetLocation(this.getSourceLocator());
            e.maybeSetContext(context);
            throw e;
      } catch(Exception e) {
        XPathException xe = new XPathException("Exception in ixsl:" + localName + "() " + e.getMessage());
            xe.maybeSetLocation(this.getSourceLocator());
            xe.maybeSetContext(context);
        throw xe;
      }

    }
   
}

// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.


TOP

Related Classes of client.net.sf.saxon.ce.js.IXSLFunction

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.