package client.net.sf.saxon.ce.lib; import client.net.sf.saxon.ce.Configuration; import client.net.sf.saxon.ce.SaxonceApi; import client.net.sf.saxon.ce.expr.instruct.Instruction; import client.net.sf.saxon.ce.om.StructuredQName; import client.net.sf.saxon.ce.style.StyleElement; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.SourceLocator; import com.google.gwt.logging.client.LogConfiguration; import com.google.gwt.user.client.Window; import java.io.PrintStream; import java.util.logging.Level; import java.util.logging.Logger; /** * <B>StandardErrorListener</B> is the standard error handler for XSLT and XQuery processing * errors, used if no other ErrorListener is nominated. * * @author Michael H. Kay */ public class StandardErrorListener implements ErrorListener{ private int recoveryPolicy = Configuration.RECOVER_WITH_WARNINGS; private int warningCount = 0; protected transient PrintStream errorOutput = System.err; private static Logger logger = Logger.getLogger("StandardErrorListener"); /** * Create a Standard Error Listener */ public StandardErrorListener() { } /** * Make a clean copy of this ErrorListener. This is necessary because the * standard error listener is stateful (it remembers how many errors there have been) * * @return a copy of this error listener */ public StandardErrorListener makeAnother() { StandardErrorListener sel = new StandardErrorListener(); sel.errorOutput = errorOutput; return sel; } // Note, when the standard error listener is used, a new // one is created for each transformation, because it holds // the recovery policy and the warning count. /** * Set output destination for error messages (default is System.err) * * @param writer The PrintStream to use for error messages */ public void setErrorOutput(PrintStream writer) { errorOutput = writer; } /** * Get the error output stream * * @return the error output stream */ public PrintStream getErrorOutput() { return errorOutput; } /** * Set the recovery policy * * @param policy the recovery policy for XSLT recoverable errors. One of * {@link Configuration#RECOVER_SILENTLY}, * {@link Configuration#RECOVER_WITH_WARNINGS}, * {@link Configuration#DO_NOT_RECOVER}. */ public void setRecoveryPolicy(int policy) { recoveryPolicy = policy; } /** * Receive notification of a warning. * <p/> * <p>Transformers can use this method to report conditions that * are not errors or fatal errors. The default behaviour is to * take no action.</p> * <p/> * <p>After invoking this method, the Transformer must continue with * the transformation. It should still be possible for the * application to process the document through to the end.</p> * * @param exception The warning information encapsulated in a * transformer exception. * @see javax.xml.transform.TransformerException */ public void warning(XPathException exception) { if (recoveryPolicy == Configuration.RECOVER_SILENTLY) { // do nothing return; } if (errorOutput == null) { // can happen after deserialization errorOutput = System.err; } String message = ""; if (exception.getLocator() != null) { message = getLocationMessage(exception) + "\n "; } message += wordWrap(getExpandedMessage(exception)); logger.log(Level.WARNING, message); errorOutput.println("Warning: " + message); warningCount++; if (warningCount > 25) { errorOutput.println("No more warnings will be displayed"); recoveryPolicy = Configuration.RECOVER_SILENTLY; warningCount = 0; } } /** * Receive notification of a non-recoverable error. * <p/> * <p>The application must assume that the transformation cannot * continue after the Transformer has invoked this method, * and should continue (if at all) only to collect * addition error messages. In fact, Transformers are free * to stop reporting events once this method has been invoked.</p> * * @param exception The error information encapsulated in a * transformer exception. * @throws XPathException if the application * chooses to discontinue the transformation. */ public void error(XPathException exception) { if (exception.hasBeenReported()) { // don't report the same error twice return; } if (errorOutput == null) { // can happen after deserialization errorOutput = System.err; } String message = "Error " + getLocationMessage(exception) + "\n " + wordWrap(getExpandedMessage(exception)); logger.log(Level.SEVERE, message); errorOutput.println(message); if (exception instanceof XPathException) { exception.setHasBeenReported(true); // probably redundant. It's the caller's job to set this flag, because there might be // a non-standard error listener in use. } } /** * Get a string identifying the location of an error. * * @param err the exception containing the location information * @return a message string describing the location */ public String getLocationMessage(XPathException err) { SourceLocator loc = err.getLocator(); while (loc == null) { if (err.getCause() instanceof XPathException) { err = (XPathException)err.getCause(); loc = err.getLocator(); } else if (err.getCause() instanceof XPathException) { err = (XPathException)err.getCause(); loc = err.getLocator(); } else { return ""; } } return getLocationMessageText(loc); } private static String getLocationMessageText(SourceLocator loc) { return "at " + loc.getLocation(); } /** * Abbreviate a URI (if requested) * @param uri the URI to be abbreviated * @return the abbreviated URI, unless full path names were requested, in which case * the URI as supplied */ public static String abbreviatePath(String uri) { if (uri == null) { return null; } int slash = uri.lastIndexOf('/'); if (slash >= 0 && slash < uri.length()-1) { return uri.substring(slash+1); } else { return uri; } } private static String getCodeMessage(StructuredQName qCode) { String codeText = ""; if (qCode != null) { String code = qCode.getLocalName(); if (code.startsWith("XTTE")){ code = code.substring(4); int q = Integer.parseInt(code); String suffix = " must match its declared type"; switch (q) { case 570: codeText = " The value of a variable" + suffix; break; case 600: codeText = " Default value of a template paremeter" + suffix; break; case 590: codeText = " Supplied value of a template parameter" + suffix; break; default: } } else if (code.startsWith("XPTY")) { code = code.substring(4); int q = Integer.parseInt(code); String suffix = " must match its declared type"; switch (q) { case 4: codeText = " The expression value is not consistent with the context in which it appears"; break; case 18: codeText = " Last step in path expression contains both nodes and atomic values"; break; case 19: codeText = " A path expression step contains an atomic value"; break; case 20: codeText = " In an axis step, the context item is not a node"; break; default: } } } return codeText; } /** * Get a string containing the message for this exception and all contained exceptions * * @param err the exception containing the required information * @return a message that concatenates the message of this exception with its contained exceptions, * also including information about the error code and location. */ public static String getExpandedMessage(XPathException err) { StructuredQName qCode; String additionalLocationText; qCode = err.getErrorCodeQName(); additionalLocationText = err.getAdditionalLocationText(); if (qCode == null && err.getCause() instanceof XPathException) { qCode = ((XPathException)err.getCause()).getErrorCodeQName(); } String message = ""; String codeText = ""; if (qCode != null) { if (qCode.getNamespaceURI().equals(NamespaceConstant.ERR)) { message = qCode.getLocalName(); } else { message = qCode.getDisplayName(); } } if (additionalLocationText != null) { message += " " + additionalLocationText; } Throwable e = err; int msgLen = message.length(); while (true) { if (e == null) { break; } String next = e.getMessage(); if (next == null) { next = ""; } if (next.startsWith("client.net.sf.saxon.ce.trans.XPathException: ")) { next = next.substring(next.indexOf(": ") + 2); } if (!message.endsWith(next)) { if (!"".equals(message) && !message.trim().endsWith(":")) { message += ": "; } message += next; } if (e instanceof XPathException) { e = e.getCause(); } else { // e.printStackTrace(); break; } } if (LogConfiguration.loggingIsEnabled()) { if (msgLen == message.length()) { String msg = getCodeMessage(qCode); if (msg.length() != 0) { message += ": " + msg; } } } return message; } /** * Extract a name identifying the instruction at which an error occurred * * @param inst the provider of information * @return the name of the containing instruction or expression, in user-meaningful terms */ private static String getInstructionName(Instruction inst) { if (inst.getSourceLocator() instanceof StyleElement) { return ((StyleElement)inst.getSourceLocator()).getDisplayName(); } else { return null; } } /** * Wordwrap an error message into lines of 72 characters or less (if possible) * * @param message the message to be word-wrapped * @return the message after applying word-wrapping */ private static String wordWrap(String message) { int nl = message.indexOf('\n'); if (nl < 0) { nl = message.length(); } if (nl > 100) { int i = 90; while (message.charAt(i) != ' ' && i > 0) { i--; } if (i > 10) { return message.substring(0, i) + "\n " + wordWrap(message.substring(i + 1)); } else { return message; } } else if (nl < message.length()) { return message.substring(0, nl) + '\n' + wordWrap(message.substring(nl + 1)); } else { return message; } } } // The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is Michael H. Kay. // // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved. // // Contributor(s): //