/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version
* 2.1 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 Lesser General Public License for more details.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.processor.transformer.xslt;
import org.orbeon.errorified.Exceptions;
import org.apache.log4j.Logger;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.common.ValidationException;
import org.orbeon.oxf.xml.dom4j.ExtendedLocationData;
import org.orbeon.oxf.xml.dom4j.LocationData;
import org.xml.sax.SAXException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMLocator;
import java.util.ArrayList;
import java.util.List;
public class StringErrorListener implements ErrorListener {
private Logger logger;
private boolean hasErrors;
private List<LocationData> errorLocationData;
private StringBuilder messages = new StringBuilder();
public StringErrorListener(Logger logger) {
this.logger = logger;
}
public void warning(TransformerException exception)
throws TransformerException {
String message = "Warning: ";
if (exception.getLocator() != null)
message += getLocationMessage(exception) + "\n ";
message += getExpandedMessage(exception);
logger.warn(message);
if (messages.length() > 0)
messages.append("\n");
messages.append(message);
}
public void error(TransformerException exception) throws TransformerException {
addError(exception);
final String locationMessage = getLocationMessage(exception);
final String message = "Error"
+ (locationMessage.length() > 0 ? " " + locationMessage + ":\n" : ": ")
+ getExpandedMessage(exception);
logger.error(message);
if (messages.length() > 0)
messages.append("\n");
messages.append(message);
}
/**
* Receive notification of a non-recoverable error.
*
* 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.
*
* @param exception The error information encapsulated in a transformer exception.
* @throws javax.xml.transform.TransformerException if the application chooses to discontinue the transformation.
* @see javax.xml.transform.TransformerException
*/
public void fatalError(TransformerException exception) throws TransformerException {
error(exception);
throw exception;
}
private void addError(TransformerException exception) {
hasErrors = true;
if (errorLocationData == null)
errorLocationData = new ArrayList<LocationData>();
final LocationData locationData = getTransformerExceptionLocationData(exception, null);
if (locationData != null)
errorLocationData.add(locationData);
}
public boolean hasErrors() {
return hasErrors;
}
public String getMessages() {
return messages.toString();
}
public List<LocationData> getErrors() {
return errorLocationData;
}
/**
* Try to get the best possible location data for a TransformerException.
*
* @param transformerException TransformerException to process
* @param defaultSystemId System Id to use if none is found in the TransformerException.
* @return ExtendedLocationData
*/
public static ExtendedLocationData getTransformerExceptionLocationData(TransformerException transformerException, String defaultSystemId) {
final SourceLocator locator = transformerException.getLocator();
if (locator == null && defaultSystemId == null) {
return null;
} else if (locator == null) {
return new ExtendedLocationData(defaultSystemId, -1, -1, null);
} else {
String description;
if (locator instanceof DOMLocator) {
description = ((DOMLocator) locator).getOriginatingNode().getNodeName();
} else if (locator.getClass().getName().equals("net.sf.saxon.instruct.InstructionDetails")
|| locator.getClass().getName().equals("org.orbeon.saxon.instruct.InstructionDetails")) {
try {
description = locator.getClass().getMethod("getInstructionName", new Class[]{}).invoke(locator).toString();
} catch (Exception e) {
// Let's not consider this a real issue, just clear the description
description = null;
}
} else {
description = null;
}
return new ExtendedLocationData((locator.getSystemId() != null) ? locator.getSystemId() : defaultSystemId, locator.getLineNumber(), locator.getColumnNumber(), description);
}
}
/**
* Get a string identifying the location of an error.
*/
private static String getLocationMessage(TransformerException te) {
final ExtendedLocationData extendedLocationData = getTransformerExceptionLocationData(te, null);
if (extendedLocationData != null) {
return "at " + extendedLocationData.toString();
} else if (te.getException() instanceof OXFException) {
final Throwable t = Exceptions.getRootThrowable(te.getException());
// TODO: check this, should maybe look for root validation data?
if (t instanceof ValidationException)
return ((ValidationException) t).firstLocationData().toString();
else
return ((OXFException) te.getException()).getMessage();
} else {
return "";
}
}
/**
* Get a string containing the message for this exception and all contained exceptions
*/
private static String getExpandedMessage(TransformerException err) {
String message = "";
Throwable e = err;
while (true) {
if (e == null) {
break;
}
String next = e.getMessage();
if (next == null) next = "";
if (!next.equals("TRaX Transform Exception") && !message.endsWith(next)) {
if (!message.equals("")) {
message += ": ";
}
message += Exceptions.getRootThrowable(e).getMessage();
}
if (e instanceof TransformerException) {
e = ((TransformerException) e).getException();
} else if (e instanceof SAXException) {
e = ((SAXException) e).getException();
} else {
break;
}
}
return message;
}
}