org.apache.xml.utils.ListingErrorHandler Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* $Id: ListingErrorHandler.java 468655 2006-10-28 07:12:06Z minchau $
*/
package org.apache.xml.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;
import org.apache.xml.res.XMLErrorResources;
import org.apache.xml.res.XMLMessages;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Sample implementation of similar SAX ErrorHandler and JAXP ErrorListener.
*
* This implementation is suitable for various use cases, and
* provides some basic configuration API's as well to control
* when we re-throw errors, etc.
*
* @author [email protected]
* @version $Id: ListingErrorHandler.java 468655 2006-10-28 07:12:06Z minchau $
* @xsl.usage general
*/
public class ListingErrorHandler implements ErrorHandler, ErrorListener
{
protected PrintWriter m_pw = null;
/**
* Constructor ListingErrorHandler; user-supplied PrintWriter.
*/
public ListingErrorHandler(PrintWriter pw)
{
if (null == pw)
throw new NullPointerException(XMLMessages.createXMLMessage(XMLErrorResources.ER_ERRORHANDLER_CREATED_WITH_NULL_PRINTWRITER, null));
// "ListingErrorHandler created with null PrintWriter!");
m_pw = pw;
}
/**
* Constructor ListingErrorHandler; uses System.err.
*/
public ListingErrorHandler()
{
m_pw = new PrintWriter(System.err, true);
}
/* ======== Implement org.xml.sax.ErrorHandler ======== */
/**
* Receive notification of a warning.
*
* SAX parsers will use this method to report conditions that
* are not errors or fatal errors as defined by the XML 1.0
* recommendation. The default behaviour is to take no action.
*
* The SAX parser must continue to provide normal parsing events
* after invoking this method: it should still be possible for the
* application to process the document through to the end.
*
* Filters may use this method to report other, non-XML warnings
* as well.
*
* @param exception The warning information encapsulated in a
* SAX parse exception.
* @exception org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception; only if setThrowOnWarning is true.
* @see org.xml.sax.SAXParseException
*/
public void warning (SAXParseException exception)
throws SAXException
{
logExceptionLocation(m_pw, exception);
// Note: should we really call .toString() below, since
// sometimes the message is not properly set?
m_pw.println("warning: " + exception.getMessage());
m_pw.flush();
if (getThrowOnWarning())
throw exception;
}
/**
* Receive notification of a recoverable error.
*
* This corresponds to the definition of "error" in section 1.2
* of the W3C XML 1.0 Recommendation. For example, a validating
* parser would use this callback to report the violation of a
* validity constraint. The default behaviour is to take no
* action.
*
* The SAX parser must continue to provide normal parsing events
* after invoking this method: it should still be possible for the
* application to process the document through to the end. If the
* application cannot do so, then the parser should report a fatal
* error even if the XML 1.0 recommendation does not require it to
* do so.
*
* Filters may use this method to report other, non-XML errors
* as well.
*
* @param exception The error information encapsulated in a
* SAX parse exception.
* @exception org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception; only if setThrowOnErroris true.
* @see org.xml.sax.SAXParseException
*/
public void error (SAXParseException exception)
throws SAXException
{
logExceptionLocation(m_pw, exception);
m_pw.println("error: " + exception.getMessage());
m_pw.flush();
if (getThrowOnError())
throw exception;
}
/**
* Receive notification of a non-recoverable error.
*
* This corresponds to the definition of "fatal error" in
* section 1.2 of the W3C XML 1.0 Recommendation. For example, a
* parser would use this callback to report the violation of a
* well-formedness constraint.
*
* The application must assume that the document is unusable
* after the parser has invoked this method, and should continue
* (if at all) only for the sake of collecting addition error
* messages: in fact, SAX parsers are free to stop reporting any
* other events once this method has been invoked.
*
* @param exception The error information encapsulated in a
* SAX parse exception.
* @exception org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception; only if setThrowOnFatalError is true.
* @see org.xml.sax.SAXParseException
*/
public void fatalError (SAXParseException exception)
throws SAXException
{
logExceptionLocation(m_pw, exception);
m_pw.println("fatalError: " + exception.getMessage());
m_pw.flush();
if (getThrowOnFatalError())
throw exception;
}
/* ======== Implement javax.xml.transform.ErrorListener ======== */
/**
* Receive notification of a warning.
*
* {@link javax.xml.transform.Transformer} can use this method to report
* conditions that are not errors or fatal errors. The default behaviour
* is to take no action.
*
* 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.
*
* @param exception The warning information encapsulated in a
* transformer exception.
*
* @throws javax.xml.transform.TransformerException only if
* setThrowOnWarning is true.
*
* @see javax.xml.transform.TransformerException
*/
public void warning(TransformerException exception)
throws TransformerException
{
logExceptionLocation(m_pw, exception);
m_pw.println("warning: " + exception.getMessage());
m_pw.flush();
if (getThrowOnWarning())
throw exception;
}
/**
* Receive notification of a recoverable error.
*
* The transformer must continue to try and provide normal transformation
* after invoking this method. It should still be possible for the
* application to process the document through to the end if no other errors
* are encountered.
*
* @param exception The error information encapsulated in a
* transformer exception.
*
* @throws javax.xml.transform.TransformerException only if
* setThrowOnError is true.
*
* @see javax.xml.transform.TransformerException
*/
public void error(TransformerException exception)
throws TransformerException
{
logExceptionLocation(m_pw, exception);
m_pw.println("error: " + exception.getMessage());
m_pw.flush();
if (getThrowOnError())
throw exception;
}
/**
* Receive notification of a non-recoverable error.
*
* The transformer must continue to try and provide normal transformation
* after invoking this method. It should still be possible for the
* application to process the document through to the end if no other errors
* are encountered, but there is no guarantee that the output will be
* useable.
*
* @param exception The error information encapsulated in a
* transformer exception.
*
* @throws javax.xml.transform.TransformerException only if
* setThrowOnError is true.
*
* @see javax.xml.transform.TransformerException
*/
public void fatalError(TransformerException exception)
throws TransformerException
{
logExceptionLocation(m_pw, exception);
m_pw.println("error: " + exception.getMessage());
m_pw.flush();
if (getThrowOnError())
throw exception;
}
/* ======== Implement worker methods ======== */
/**
* Print out location information about the exception.
*
* Cribbed from DefaultErrorHandler.printLocation()
* @param pw PrintWriter to send output to
* @param exception TransformerException or SAXParseException
* to log information about
*/
public static void logExceptionLocation(PrintWriter pw, Throwable exception)
{
if (null == pw)
pw = new PrintWriter(System.err, true);
SourceLocator locator = null;
Throwable cause = exception;
// Try to find the locator closest to the cause.
do
{
// Find the current locator, if one present
if(cause instanceof SAXParseException)
{
// A SAXSourceLocator is a Xalan helper class
// that implements both a SourceLocator and a SAX Locator
//@todo check that the new locator actually has
// as much or more information as the
// current one already does
locator = new SAXSourceLocator((SAXParseException)cause);
}
else if (cause instanceof TransformerException)
{
SourceLocator causeLocator = ((TransformerException)cause).getLocator();
if(null != causeLocator)
{
locator = causeLocator;
}
}
// Then walk back down the chain of exceptions
if(cause instanceof TransformerException)
cause = ((TransformerException)cause).getCause();
else if(cause instanceof WrappedRuntimeException)
cause = ((WrappedRuntimeException)cause).getException();
else if(cause instanceof SAXException)
cause = ((SAXException)cause).getException();
else
cause = null;
}
while(null != cause);
// Formatting note: mimic javac-like errors:
// path\filename:123: message-here
// systemId:L=1;C=2: message-here
if(null != locator)
{
String id = (locator.getPublicId() != locator.getPublicId())
? locator.getPublicId()
: (null != locator.getSystemId())
? locator.getSystemId() : "SystemId-Unknown";
pw.print(id + ":Line=" + locator.getLineNumber()
+ ";Column=" + locator.getColumnNumber()+": ");
pw.println("exception:" + exception.getMessage());
pw.println("root-cause:"
+ ((null != cause) ? cause.getMessage() : "null"));
logSourceLine(pw, locator);
}
else
{
pw.print("SystemId-Unknown:locator-unavailable: ");
pw.println("exception:" + exception.getMessage());
pw.println("root-cause:"
+ ((null != cause) ? cause.getMessage() : "null"));
}
}
/**
* Print out the specific source line that caused the exception,
* if possible to load it.
*
* @param pw PrintWriter to send output to
* @param locator Xalan wrapper for either a JAXP or a SAX
* source location object
*/
public static void logSourceLine(PrintWriter pw, SourceLocator locator)
{
if (null == locator)
return;
if (null == pw)
pw = new PrintWriter(System.err, true);
String url = locator.getSystemId();
// Bail immediately if we get SystemId-Unknown
//@todo future improvement: attempt to get resource
// from a publicId if possible
if (null == url)
{
pw.println("line: (No systemId; cannot read file)");
pw.println();
return;
}
//@todo attempt to get DOM backpointer or other ids
try
{
int line = locator.getLineNumber();
int column = locator.getColumnNumber();
pw.println("line: " + getSourceLine(url, line));
StringBuffer buf = new StringBuffer("line: ");
for (int i = 1; i < column; i++)
{
buf.append(' ');
}
buf.append('^');
pw.println(buf.toString());
}
catch (Exception e)
{
pw.println("line: logSourceLine unavailable due to: " + e.getMessage());
pw.println();
}
}
/**
* Return the specific source line that caused the exception,
* if possible to load it; allow exceptions to be thrown.
*
* @author [email protected]
*/
protected static String getSourceLine(String sourceUrl, int lineNum)
throws Exception
{
URL url = null;
// Get a URL from the sourceUrl
try
{
// Try to get a URL from it as-is
url = new URL(sourceUrl);
}
catch (java.net.MalformedURLException mue)
{
int indexOfColon = sourceUrl.indexOf(':');
int indexOfSlash = sourceUrl.indexOf('/');
if ((indexOfColon != -1)
&& (indexOfSlash != -1)
&& (indexOfColon < indexOfSlash))
{
// The url is already absolute, but we could not get
// the system to form it, so bail
throw mue;
}
else
{
// The url is relative, so attempt to get absolute
url = new URL(SystemIDResolver.getAbsoluteURI(sourceUrl));
// If this fails, allow the exception to propagate
}
}
String line = null;
InputStream is = null;
BufferedReader br = null;
try
{
// Open the URL and read to our specified line
URLConnection uc = url.openConnection();
is = uc.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
// Not the most efficient way, but it works
// (Feel free to patch to seek to the appropriate line)
for (int i = 1; i <= lineNum; i++)
{
line = br.readLine();
}
}
// Allow exceptions to propagate from here, but ensure
// streams are closed!
finally
{
br.close();
is.close();
}
// Return whatever we found
return line;
}
/* ======== Implement settable properties ======== */
/**
* User-settable behavior: when to re-throw exceptions.
*
* This allows per-instance configuration of
* ListingErrorHandlers. You can ask us to either throw
* an exception when we're called for various warning /
* error / fatalErrors, or simply log them and continue.
*
* @param b if we should throw an exception on warnings
*/
public void setThrowOnWarning(boolean b)
{
throwOnWarning = b;
}
/**
* User-settable behavior: when to re-throw exceptions.
*
* @return if we throw an exception on warnings
*/
public boolean getThrowOnWarning()
{
return throwOnWarning;
}
/** If we should throw exception on warnings; default:false. */
protected boolean throwOnWarning = false;
/**
* User-settable behavior: when to re-throw exceptions.
*
* This allows per-instance configuration of
* ListingErrorHandlers. You can ask us to either throw
* an exception when we're called for various warning /
* error / fatalErrors, or simply log them and continue.
*
* Note that the behavior of many parsers/transformers
* after an error is not necessarily defined!
*
* @param b if we should throw an exception on errors
*/
public void setThrowOnError(boolean b)
{
throwOnError = b;
}
/**
* User-settable behavior: when to re-throw exceptions.
*
* @return if we throw an exception on errors
*/
public boolean getThrowOnError()
{
return throwOnError;
}
/** If we should throw exception on errors; default:true. */
protected boolean throwOnError = true;
/**
* User-settable behavior: when to re-throw exceptions.
*
* This allows per-instance configuration of
* ListingErrorHandlers. You can ask us to either throw
* an exception when we're called for various warning /
* error / fatalErrors, or simply log them and continue.
*
* Note that the behavior of many parsers/transformers
* after a fatalError is not necessarily defined, most
* products will probably barf if you continue.
*
* @param b if we should throw an exception on fatalErrors
*/
public void setThrowOnFatalError(boolean b)
{
throwOnFatalError = b;
}
/**
* User-settable behavior: when to re-throw exceptions.
*
* @return if we throw an exception on fatalErrors
*/
public boolean getThrowOnFatalError()
{
return throwOnFatalError;
}
/** If we should throw exception on fatalErrors; default:true. */
protected boolean throwOnFatalError = true;
}