/*
ESXX - The friendly ECMAscript/XML Application Server
Copyright (C) 2007-2015 Martin Blom <martin@blom.org>
This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.esxx.saxon;
import net.sf.saxon.dom.ElementOverNodeInfo;
import net.sf.saxon.dom.DOMWriter;
import net.sf.saxon.expr.*;
import net.sf.saxon.om.*;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.*;
import org.esxx.ESXX;
import org.esxx.ESXXException;
import org.esxx.util.JS;
import org.mozilla.javascript.*;
import org.w3c.dom.Document;
import org.w3c.dom.UserDataHandler;
import org.w3c.dom.Node;
import java.util.Collection;
public class ESXXExpression
extends SimpleExpression {
private String object;
private String method;
public ESXXExpression(String object, String method, Expression[] args) {
super();
this.object = object;
this.method = method;
setArguments(args);
}
@Override public int getImplementationMethod() {
return ITERATE_METHOD;
}
@Override public SequenceIterator iterate(XPathContext context)
throws XPathException {
Context cx = Context.getCurrentContext();
Scriptable scope = (Scriptable) cx.getThreadLocal(ESXXExpression.class);
Object[] args = new Object[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
int mode = ExpressionTool.lazyEvaluationMode(arguments[i]);
ValueRepresentation v = ExpressionTool.evaluate(arguments[i], mode, context, 1);
args[i] = contvertToJS(v, context, cx, scope);
}
Object result = JS.callJSMethod(object, method, args, "XSLT Stylesheet", cx, scope, true);
if (result instanceof NativeArray) {
result = cx.getElements((NativeArray) result);
}
if (result instanceof org.mozilla.javascript.xml.XMLObject) {
result = ESXX.e4xToDOM((Scriptable) result);
}
Value value = Value.convertJavaObjectToXPath(result, SequenceType.ANY_SEQUENCE, context);
return value.iterate();
}
private static Object contvertToJS(Object value, XPathContext context,
Context cx, Scriptable scope)
throws XPathException {
if (value instanceof Value) {
value = ((Value) value).convertToJava(Object.class, context);
}
if (value instanceof NodeInfo) {
NodeInfo ni = (NodeInfo) value;
if (ni.getNodeKind() == Type.ELEMENT) {
value = ESXX.domToE4X(new ElementWrapper(ni), cx, scope);
}
else {
value = ni.getStringValue();
}
}
if (value instanceof Collection<?>) {
value = ((Collection<?>) value).toArray();
}
if (value instanceof Object[]) {
Object[] array = (Object[]) value;
for (int i = 0; i < array.length; ++i) {
array[i] = contvertToJS(array[i], context, cx, scope);
}
value = cx.newArray(scope, array);
}
return value;
}
private static class ElementWrapper
extends ElementOverNodeInfo {
private String key;
private Object data;
private UserDataHandler handler;
ElementWrapper(NodeInfo ni) {
super();
node = ni;
}
// Rhino depends on this method
@Override public Object setUserData(String key, Object data, UserDataHandler handler) {
this.key = key;
this.data = data;
this.handler = handler;
return data;
}
// Rhino depends on this method
@SuppressWarnings("unused") public Object getUserData() {
return data;
}
// Rhino depends on this method
@Override public Node cloneNode(boolean deep) {
if (!deep) {
throw new UnsupportedOperationException();
}
try {
// Convert the node to a "real" DOM node
ESXX esxx = ESXX.getInstance();
Document doc = esxx.createDocument("ugly");
DOMWriter dom_writer = new DOMWriter();
dom_writer.setPipelineConfiguration(esxx.
getSaxonProcessor().
getUnderlyingConfiguration().
makePipelineConfiguration());
dom_writer.setNode(doc.getDocumentElement());
node.copy(dom_writer, NodeInfo.ALL_NAMESPACES, false, 0);
// The returned node is not supposed to be part of a document
Node ugly = doc.getDocumentElement();
Node result = ugly.removeChild(ugly.getFirstChild());
// Call installed handler
if (handler != null) {
handler.handle(UserDataHandler.NODE_CLONED, key, data, this, result);
}
return result;
}
catch (XPathException ex) {
throw new ESXXException("ElementWrapper.cloneNode() failed: " + ex.getMessage(), ex);
}
}
}
static final long serialVersionUID = 5149528466515089486L;
}