package org.jaggeryjs.hostobjects.xslt;
/*
* Copyright 2006,2007 WSO2, Inc. http://www.wso2.org
*
* 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.
*/
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jaggeryjs.scriptengine.engine.RhinoEngine;
import org.jaggeryjs.scriptengine.exceptions.ScriptException;
import org.jaggeryjs.scriptengine.util.HostObjectUtil;
import org.mozilla.javascript.*;
import org.mozilla.javascript.xml.XMLObject;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* var xslt = new XSLT(xslt);
*/
public class XSLTHostObject extends ScriptableObject {
private static final Log log = LogFactory.getLog(XSLTHostObject.class);
private TransformerFactory factory = null;
private Transformer transformer = null;
private StringReader xslt = null;
private static final String hostObjectName = "XSLT";
/**
* XSLT(xslt)
* XSLT(xslt, paramMap)
* XSLT(xslt, uriResolver)
* XSLT(xslt, paramMap, uriResolver)
*/
public static Scriptable jsConstructor(final Context cx, Object[] args, final Function ctorObj,
boolean inNewExpr) throws ScriptException {
int argsCount = args.length;
if (argsCount < 1 || argsCount > 3) {
HostObjectUtil.invalidNumberOfArgs(hostObjectName, hostObjectName, argsCount, true);
}
Function uriResolver = null;
NativeObject paramMap = null;
XSLTHostObject xho = new XSLTHostObject();
if (args[0] instanceof String) {
xho.xslt = new StringReader((String) args[0]);
} else if (args[0] instanceof XMLObject) {
xho.xslt = new StringReader(args[0].toString());
} else {
HostObjectUtil.invalidArgsError(hostObjectName, hostObjectName, "1", "string | xml", args[0], true);
}
if (argsCount == 2) {
if (args[1] instanceof NativeObject) {
paramMap = (NativeObject) args[1];
} else if (args[1] instanceof Function) {
uriResolver = (Function) args[1];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, hostObjectName, "2", "string", args[1], true);
}
} else if (argsCount == 3) {
if (args[1] instanceof NativeObject) {
paramMap = (NativeObject) args[1];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, hostObjectName, "2", "string", args[1], true);
}
if (args[2] instanceof Function) {
uriResolver = (Function) args[2];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, hostObjectName, "3", "string", args[1], true);
}
}
xho.factory = TransformerFactory.newInstance();
if (uriResolver != null) {
xho.factory.setURIResolver(getUriResolver(cx, ctorObj, uriResolver));
}
return xho;
}
private static URIResolver getUriResolver(final Context cx, final Scriptable scope, final Function uriResolver) {
return new URIResolver() {
@Override
public Source resolve(String href, String base) throws TransformerException {
Object obj = uriResolver.call(cx, scope, scope, new Object[]{base, href});
if (obj instanceof XMLObject) {
return new StreamSource(new StringReader(obj.toString()));
} else if (obj instanceof String) {
return new StreamSource(new StringReader((String) obj));
} else {
throw new TransformerException("Error while resolving the content for " + href);
}
}
};
}
private static Transformer getTransformer(Context cx, Scriptable scope, XSLTHostObject xho, NativeObject paramMap, Function uriResolver) throws ScriptException {
Transformer transformer;
try {
transformer = xho.factory.newTransformer(new StreamSource(xho.xslt));
if (paramMap != null) {
setParams(transformer, paramMap);
}
if (uriResolver != null) {
transformer.setURIResolver(getUriResolver(cx, scope, uriResolver));
}
return transformer;
} catch (TransformerConfigurationException e) {
log.error(e.getMessage(), e);
throw new ScriptException(e);
}
}
public String getClassName() {
return hostObjectName;
}
/**
* transform(xml)
* transform(xml, callback)
* transform(xml, paramMap)
* transform(xml, paramMap, callback)
* trasformer(xml, callback, uriResolver)
* trasformer(xml, paramMap, callback, uriResolver)
*/
public static String jsFunction_transform(final Context cx, final Scriptable thisObj, Object[] args, Function funObj)
throws ScriptException {
String functionName = "transform";
int argsCount = args.length;
if (argsCount > 4) {
HostObjectUtil.invalidNumberOfArgs(hostObjectName, functionName, argsCount, false);
}
StringReader xml = null;
Function uriResolver = null;
NativeObject paramMap = null;
Function callback = null;
if (args[0] instanceof String) {
xml = new StringReader((String) args[0]);
} else if (args[0] instanceof XMLObject) {
xml = new StringReader(args[0].toString());
} else {
HostObjectUtil.invalidArgsError(hostObjectName, hostObjectName, "1", "string | xml", args[0], true);
}
if (argsCount == 2) {
if (args[1] instanceof NativeObject) {
paramMap = (NativeObject) args[1];
} else if (args[1] instanceof Function) {
callback = (Function) args[1];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "2", "object | function", args[1], false);
}
} else if (argsCount == 3) {
if (args[1] instanceof NativeObject) {
paramMap = (NativeObject) args[1];
if (args[2] instanceof Function) {
callback = (Function) args[2];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "3", "function", args[2], false);
}
} else if (args[1] instanceof Function) {
callback = (Function) args[1];
if (args[2] instanceof Function) {
uriResolver = (Function) args[2];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "3", "function", args[2], false);
}
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "2", "object", args[1], false);
}
} else if (argsCount == 4) {
if (args[1] instanceof NativeObject) {
paramMap = (NativeObject) args[1];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "2", "object", args[1], false);
}
if (args[2] instanceof Function) {
callback = (Function) args[2];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "3", "function", args[2], false);
}
if (args[3] instanceof Function) {
uriResolver = (Function) args[3];
} else {
HostObjectUtil.invalidArgsError(hostObjectName, functionName, "4", "function", args[3], false);
}
}
final XSLTHostObject xho = (XSLTHostObject) thisObj;
try {
final StringWriter result = new StringWriter();
if (callback == null) {
if (xho.transformer == null) {
xho.transformer = getTransformer(cx, thisObj, xho, paramMap, uriResolver);
} else {
if (paramMap != null) {
setParams(xho.transformer, paramMap);
}
if (uriResolver != null) {
xho.transformer.setURIResolver(getUriResolver(cx, funObj, uriResolver));
}
}
xho.transformer.transform(new StreamSource(xml), new StreamResult(result));
return result.toString();
}
final StringReader finalXml = xml;
final Function finalCallback = callback;
final Function finalUriResolver = uriResolver;
final NativeObject finalParamMap = paramMap;
final ContextFactory factory = cx.getFactory();
final ExecutorService es = Executors.newCachedThreadPool();
es.submit(new Callable() {
public Object call() throws Exception {
Context ctx = RhinoEngine.enterContext(factory);
try {
getTransformer(ctx, thisObj, xho, finalParamMap, finalUriResolver).transform(
new StreamSource(finalXml), new StreamResult(result));
finalCallback.call(ctx, xho, xho, new Object[]{result.toString()});
} catch (TransformerException e) {
log.warn(e);
} finally {
es.shutdown();
RhinoEngine.exitContext();
}
return null;
}
});
return null;
} catch (TransformerException e) {
log.error(e.getMessage(), e);
throw new ScriptException(e);
}
}
private static void setParams(Transformer transformer, NativeObject paramMap) {
transformer.clearParameters();
Object[] ids = paramMap.getIds();
for (Object id : ids) {
String key = (String) id;
Object obj = paramMap.get(key, paramMap);
transformer.setParameter(key, HostObjectUtil.serializeObject(obj));
}
}
}