package client.net.sf.saxon.ce;
import java.util.logging.Logger;
import client.net.sf.saxon.ce.dom.XMLDOM;
import client.net.sf.saxon.ce.om.DocumentInfo;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.util.URI;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Node;
import com.google.gwt.logging.client.LogConfiguration;
import com.google.gwt.user.client.Window;
/**
* A class to provide static utiltiy functions for the JavaScript API. All API
* functions must be registered within the JSNI <code>register</code> method.
* Notes: GWT-Exporter not needed here because registering static methods is
* quite straightforwards using JSNI, plus the Eclipse plug-in provides features
* such as auto-complete which makes defining function signatures simpler.
*/
public class SaxonceApi {
private static boolean processorWasJsInitiated = false;
public static void setProcessorWasJsInitiated() {
processorWasJsInitiated = true;
}
/**
* @return boolean to indicated to logging and other code that the
* XSLTProcessor was initiated from the JavaScript API
*/
public static boolean doThrowJsExceptions() {
return processorWasJsInitiated && (handler == null);
}
// API
public static JavaScriptObject requestXML(String URI) throws Exception {
try {
String pageHref = Window.Location.getHref();
String absSourceURI = (new URI(pageHref).resolve(URI)).toString();
return createAsyncDoc(absSourceURI);
} catch (Exception e) {
// re-throw exception back to calling JavaScript
throw (e);
}
}
/**
* Returns a DocumentInfo object that wraps a XML DOM document.
*
* If the JavaScript object passed as a parameter is not a DOM document, but
* simply a place-holder, then the Document is first fetched synchronously
* before wrapping.
*
* @param obj
* the DOM document or a place-holder
* @param config
* The Saxon-CE configuration
* @return a DocumentInfo object
*/
public static DocumentInfo getDocSynchronously(JavaScriptObject obj,
Configuration config) throws XPathException {
String absSourceURI = getAsyncUri(obj);
Document doc;
try {
if (absSourceURI != null) {
try {
String xml = XMLDOM.makeHTTPRequest(absSourceURI);
doc = (Document) XMLDOM.parseXML(xml);
} catch (Exception e) {
throw new XPathException(
"Synchronous HTTP GET failed for: " + absSourceURI);
}
} else {
doc = (Document) obj;
}
// check there's a document element
if (doc.getDocumentElement() == null) {
throw new XPathException("no document element");
}
} catch (Exception e) {
throw new XPathException("Error resolving document: "
+ e.getMessage());
}
return (DocumentInfo) config.wrapXMLDocument(doc, absSourceURI);
}
/**
* Registers static methods of this SaxonApi class and the LogController
* class as static methods for use in the JavaScript API, within the Saxonce
* namespace. This method must be called when the Saxonce GWT module is
* first loaded.
*/
public static native void register() /*-{
$wnd.Saxon = {};
$wnd.Saxon.requestXML = $entry(function(url) {
return @client.net.sf.saxon.ce.SaxonceApi::requestXML(Ljava/lang/String;)(url)
});
$wnd.Saxon.parseXML = $entry(function(text) {
return @client.net.sf.saxon.ce.dom.XMLDOM::parseXML(Ljava/lang/String;)(text)
});
$wnd.Saxon.serializeXML = $entry(function(node) {
return @client.net.sf.saxon.ce.dom.XMLDOM::serializeXML(Lcom/google/gwt/dom/client/Node;)(node)
});
$wnd.Saxon.setErrorHandler = $entry(function(handler) {
return @client.net.sf.saxon.ce.SaxonceApi::setErrorHandler(Lcom/google/gwt/core/client/JavaScriptObject;)(handler)
});
$wnd.Saxon.setLogLevel = $entry(function(level) {
return @client.net.sf.saxon.ce.LogController::setLogLevel(Ljava/lang/String;)(level)
});
$wnd.Saxon.getLogLevel = $entry(function() {
return @client.net.sf.saxon.ce.LogController::getLogLevel()()
});
$wnd.Saxon.newXSLT20Processor = function(doc) {
var sp = new $wnd.Saxonce.XSLT20Processor(doc);
sp.setThis(sp);
return sp;
};
$wnd.Saxon.getErrorHandler = $entry(function() {
return @client.net.sf.saxon.ce.SaxonceApi::getErrorHandler()()
});
$wnd.Saxon.run = $entry(function(cmd) {
return @client.net.sf.saxon.ce.SaxonceApi::runCommand(Lcom/google/gwt/core/client/JavaScriptObject;)(cmd)
});
$wnd.Saxon.getVersion = $entry(function() {
return @client.net.sf.saxon.ce.Version::getProductVersion()()
});
}-*/;
/**
* Factory method for JavaScript API to create new XSLT20Processor Converts
* this to new Saxonce.XSLT20Processor(doc)
*/
public static native JavaScriptObject newXSLT20Processor(
JavaScriptObject doc) /*-{
return @client.net.sf.saxon.ce.XSLT20Processor::new(Lcom/google/gwt/core/client/JavaScriptObject;)(doc);
}-*/;
private static JavaScriptObject handler = null;
/**
* API call to set the function used by <code>initCallback()</code> to make
* a JavaScript callback when a logging event occurs
*
* @param handlerFunction
* - An instance of a JavaScript function
*/
public static void setErrorHandler(JavaScriptObject handlerFunction) {
handler = handlerFunction;
}
/**
* Public API function - but also used internally Return the function object
* thats the error handler. This is the external error handler (set by a
* hosting editor for example) of if there is none then the error handler
* set using <code>setErrorHandler()
*/
public static JavaScriptObject getErrorHandler() {
return handler;
}
public static void setAnyExternalErrorHandler() {
logHandlerExternal = callExternalErrorHandler(
Version.getProductTitle(), "INIT");
}
private static boolean logHandlerExternal = false;
public static boolean isLogHandlerExternal() {
return logHandlerExternal;
}
private static native boolean callExternalErrorHandler(String message,
String level) /*-{
if ($wnd.external) {
try {
$wnd.external.saxonErrorHandler(message, level);
return true;
} catch (e) {
return false;
}
} else {
return false;
}
}-*/;
/**
* Calls back into the JavaScript calling code to allow logging of errors
* and events from within JavaScript
*
* @param message
* - The error message
* @param errorType
* - The error type - can be any type used by GWT-Logging
*/
public static void makeCallback(String message, String errorType,
String milliseconds) {
JavaScriptObject currentHandler = getErrorHandler();
setErrorMessage(message);
if (currentHandler == null && !isLogHandlerExternal())
return;
if (!callbackErrorReported) {
boolean success = false;
if (currentHandler != null) {
JavaScriptObject evt = createEventObject(message, errorType,
milliseconds);
success = initCallback(currentHandler, evt);
logAnyCallbackError(success, "JS");
}
if (isLogHandlerExternal()) {
success = callExternalErrorHandler(message, errorType);
logAnyCallbackError(success, "Ext");
}
}
}
public static void logAnyCallbackError(boolean success, String name) {
if (LogConfiguration.loggingIsEnabled() && !success) {
callbackErrorReported = true; // prevent recursion
Logger.getLogger("HandlerCallback").severe(
"Exception on " + name + " errorHandler callback");
}
}
static boolean callbackErrorReported = false;
/**
* Creates an event object for use in the JavaScript API callback
*
* @param message
* The event message
* @param errorType
* The event type e.g. FINE
* @return the event object
*/
private static native JavaScriptObject createEventObject(String message,
String errorType, String milliseconds) /*-{
var eventObj = {}
eventObj.message = message;
eventObj.level = errorType;
eventObj.time = milliseconds;
return eventObj;
}-*/;
private static native boolean initCallback(JavaScriptObject handlerFn,
JavaScriptObject eventObj) /*-{
try {
handlerFn.call(this, eventObj);
return true;
} catch (e) {
return false;
}
}-*/;
private static native void setErrorMessage(String msg) /*-{
if ($wnd.Saxon) {
$wnd.Saxon.message = msg;
} else {
$wnd.SaxonMessage = msg;
}
}-*/;
public static native JavaScriptObject createAsyncDoc(String URI) /*-{
var docObj = {}
docObj.asyncUri = URI;
return docObj;
}-*/;
public static native String getAsyncUri(JavaScriptObject obj) /*-{
if (obj.asyncUri) {
return obj.asyncUri;
} else {
return null;
}
}-*/;
public static native JavaScriptObject runCommand(JavaScriptObject cmd) /*-{
var proc = $wnd.Saxon.newXSLT20Processor();
var methodVal = null;
var sourceVal = null;
var sourceDoc = null;
var result = null;
for ( var p in cmd) {
if (cmd.hasOwnProperty(p)) {
var pValue = cmd[p];
switch (p) {
case "baseOutputURI":
proc.setBaseOutputURI(pValue);
break;
case "initialMode":
proc.setInitialMode(pValue);
break;
case "initialTemplate":
proc.setInitialTemplate(pValue);
break;
case "stylesheet":
if (typeof pValue == 'string' || pValue instanceof String) {
try {
var s = $wnd.Saxon.requestXML(pValue);
proc.importStylesheet(s);
} catch (e) {
throw "Saxon.run error: on importing stylesheet "
+ pValue;
}
} else {
proc.importStylesheet(pValue); //assume Document or RequestDocument
}
break;
case "logLevel":
$wnd.Saxon.setLogLevel(pValue);
break;
case "errorHandler":
$wnd.Saxon.setErrorHandler(pValue);
break;
case "method":
methodVal = pValue;
break;
case "success":
proc.setSuccess(pValue);
break;
case "source":
if (typeof pValue == 'string' || pValue instanceof String) {
sourceDoc = $wnd.Saxon.requestXML(pValue);
} else {
sourceDoc = pValue;
}
break;
case "parameters":
for ( var x in pValue) {
if (pValue.hasOwnProperty(x)) {
proc.setParameter(null, x, pValue[x]);
}
}
break;
} //switch
} //if
} // for
if (methodVal == "transformToFragment") {
proc.transformToFragment(sourceDoc, null);
} else if (methodVal == "transformToHTMLFragment") {
proc.transformToHTMLFragment(sourceDoc, null);
} else if (methodVal == "transformToDocument") {
proc.transformToDocument(sourceDoc);
} else {
proc.updateHTMLDocument(sourceDoc, null);
}
return proc;
}-*/;
}
// 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.