blob: 5ae34a31dd313dc7d3df1ca8e2d850571eb65bce [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* 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.
*/
package org.apache.xmlbeans;
import javax.xml.stream.Location;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
/**
* Represents a message at a specific XML location.
* <p>
* The message can be an error, warning, or simple information, and
* it may optionally be associated with a specific location in
* an XML document. The class includes methods for extracting
* the location as a line number, XmlCursor, or XmlObject, as
* well as for obtaining and message and severity of the
* error.
*
* @see XmlOptions#setErrorListener
* @see XmlException
*/
public class XmlError implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private static final ResourceBundle _bundle = PropertyResourceBundle.getBundle("org.apache.xmlbeans.message", Locale.ROOT);
private final String _message;
private final String _code;
private final String _source;
private final int _severity;
private final int _line;
private final int _column;
private int _offset = -1;
private transient XmlCursor _cursor;
/**
* Copy constructor.
*
* @param src The original XmlError to copy.
*/
public XmlError(XmlError src) {
_message = src.getMessage();
_code = src.getErrorCode();
_severity = src.getSeverity();
_source = src.getSourceName();
_line = src.getLine();
_column = src.getColumn();
_offset = src.getOffset();
_cursor = src.getCursorLocation();
}
/**
* The static factory methods should be used instead of
* this constructor.
*/
private XmlError(String message, String code, int severity,
String source, int line, int column, int offset, XmlCursor cursor) {
_message = message;
_code = code;
_severity = severity;
_source = source;
_line = line;
_column = column;
_offset = offset;
_cursor = cursor;
}
private XmlError(String code, Object[] args, int severity,
String source, int line, int column, int offset, XmlCursor cursor) {
this(XmlError.formattedMessage(code, args), code, severity, source, line, column, offset, cursor);
}
/**
* The static factory methods should be used instead of
* this constructor.
*/
protected XmlError(String message, String code, int severity, XmlCursor cursor) {
String source = null;
int line = -1;
int column = -1;
int offset = -1;
if (cursor != null) {
// Hunt down the line/column/offset
source = cursor.documentProperties().getSourceName();
XmlCursor c = cursor.newCursor();
XmlLineNumber ln =
(XmlLineNumber) c.getBookmark(XmlLineNumber.class);
if (ln == null) {
ln = (XmlLineNumber) c.toPrevBookmark(XmlLineNumber.class);
}
if (ln != null) {
line = ln.getLine();
column = ln.getColumn();
offset = ln.getOffset();
}
c.dispose();
}
_message = message;
_code = code;
_severity = severity;
_source = source;
_line = line;
_column = column;
_offset = offset;
_cursor = cursor;
}
protected XmlError(String code, Object[] args, int severity, XmlCursor cursor) {
this(XmlError.formattedMessage(code, args), code, severity, cursor);
}
/**
* The static factory methods should be used instead of
* this constructor.
*/
protected XmlError(String message, String code, int severity, Location loc) {
String source = null;
int line = -1;
int column = -1;
if (loc != null) {
line = loc.getLineNumber();
column = loc.getColumnNumber();
source = loc.getPublicId();
if (source == null) {
source = loc.getSystemId();
}
}
_message = message;
_code = code;
_severity = severity;
_source = source;
_line = line;
_column = column;
}
protected XmlError(String code, Object[] args, int severity, Location loc) {
this(XmlError.formattedMessage(code, args), code, severity, loc);
}
/**
* Returns an XmlError for the given message, with no location and {@link #SEVERITY_ERROR}.
*
* @param message the error message
*/
public static XmlError forMessage(String message) {
return forMessage(message, SEVERITY_ERROR);
}
/**
* Returns an XmlError for the given message, with no location and the given severity.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
*/
public static XmlError forMessage(String message, int severity) {
return forSource(message, severity, null);
}
/**
* Returns an XmlError for the given message, with no location and the given severity.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
*/
public static XmlError forMessage(String code, Object[] args) {
return forSource(code, args, SEVERITY_ERROR, null);
}
/**
* Returns an XmlError for the given message, with no location and the given severity.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
*/
public static XmlError forMessage(String code, Object[] args, int severity) {
return forSource(code, args, severity, null);
}
/**
* Returns an XmlError for the given message, located in the given file and {@link #SEVERITY_ERROR}.
*
* @param message the error message
* @param sourceName the URL or other name for the file
*/
public static XmlError forSource(String message, String sourceName) {
return forLocation(message, SEVERITY_ERROR, sourceName, -1, -1, -1);
}
/**
* Returns an XmlError for the given message, with the given severity, located in the given file.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param sourceName the URL or other name for the file
*/
public static XmlError forSource(String message, int severity, String sourceName) {
return forLocation(message, severity, sourceName, -1, -1, -1);
}
/**
* Returns an XmlError for the given message, with the given severity, located in the given file.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param sourceName the URL or other name for the file
*/
public static XmlError forSource(String code, Object[] args, int severity, String sourceName) {
return forLocation(code, args, severity, sourceName, -1, -1, -1);
}
/**
* Returns an XmlError for the given message, located at a specific point in the given file and {@link #SEVERITY_ERROR}.
*
* @param message the error message
* @param sourceName the URL or other name for the file
* @param location the location from an xml stream
*/
public static XmlError forLocation(String message, String sourceName, Location location) {
return new XmlError(message, (String) null, SEVERITY_ERROR, sourceName,
location.getLineNumber(), location.getColumnNumber(), -1, null);
}
/**
* Returns an XmlError for the given message, located at a specific point in the given file and {@link #SEVERITY_ERROR}.
*
* @param message the error message
* @param sourceName the URL or other name for the file
* @param line the 1-based line number, or -1 if not known
* @param column the 1-based column number, or -1 if not known
* @param offset the 0-base file character offset, or -1 if not known
*/
public static XmlError forLocation(String message, String sourceName, int line, int column, int offset) {
return new XmlError(message, (String) null, SEVERITY_ERROR, sourceName, line, column, offset, null);
}
/**
* Returns an XmlError for the given message, with the given severity, located at a specific point in the given file.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param sourceName the URL or other name for the file
* @param line the 1-based line number, or -1 if not known
* @param column the 1-based column number, or -1 if not known
* @param offset the 0-base file character offset, or -1 if not known
*/
public static XmlError forLocation(String code, Object[] args, int severity, String sourceName, int line, int column, int offset) {
return new XmlError(code, args, severity, sourceName, line, column, offset, null);
}
/**
* Returns an XmlError for the given message, with the given severity, located at a specific point in the given file.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param sourceName the URL or other name for the file
* @param line the 1-based line number, or -1 if not known
* @param column the 1-based column number, or -1 if not known
* @param offset the 0-base file character offset, or -1 if not known
*/
public static XmlError forLocation(String message, int severity, String sourceName, int line, int column, int offset) {
return new XmlError(message, (String) null, severity, sourceName, line, column, offset, null);
}
/**
* Returns an XmlError for the given message, with the given severity, located at the given physcial location and XmlCursor.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param sourceName the URL or other name for the file
* @param line the 1-based line number, or -1 if not known
* @param column the 1-based column number, or -1 if not known
* @param offset the 0-base file character offset, or -1 if not known
* @param cursor the XmlCursor representing the location of the error
*/
public static XmlError forLocationAndCursor(String message, int severity, String sourceName, int line, int column, int offset, XmlCursor cursor) {
return new XmlError(message, (String) null, severity, sourceName, line, column, offset, cursor);
}
/**
* Returns an XmlError for the given message, located at the XmlObject, with {@link #SEVERITY_ERROR}.
*
* @param message the error message
* @param xobj the XmlObject representing the location of the error
*/
public static XmlError forObject(String message, XmlObject xobj) {
return forObject(message, SEVERITY_ERROR, xobj);
}
/**
* Returns an XmlError for the given message, located at the XmlObject, with {@link #SEVERITY_ERROR}.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param xobj the XmlObject representing the location of the error
*/
public static XmlError forObject(String code, Object[] args, XmlObject xobj) {
return forObject(code, args, SEVERITY_ERROR, xobj);
}
/**
* Returns an XmlError for the given message, with the given severity, located at the XmlObject.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param xobj the XmlObject representing the location of the error
*/
public static XmlError forObject(String message, int severity, XmlObject xobj) {
if (xobj == null) {
return forMessage(message, severity);
}
XmlCursor cur = xobj.newCursor();
return forCursor(message, severity, cur);
}
/**
* Returns an XmlError for the given message, with the given severity, located at the XmlObject.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param xobj the XmlObject representing the location of the error
*/
public static XmlError forObject(String code, Object[] args, int severity, XmlObject xobj) {
if (xobj == null) {
return forMessage(code, args, severity);
}
XmlCursor cur = xobj.newCursor();
return forCursor(code, args, severity, cur);
}
/**
* Returns an XmlError for the given message, located at the XmlCursor, with {@link #SEVERITY_ERROR}.
*
* @param message the error message
* @param cursor the XmlCursor representing the location of the error
*/
public static XmlError forCursor(String message, XmlCursor cursor) {
return forCursor(message, SEVERITY_ERROR, cursor);
}
/**
* Returns an XmlError for the given message, located at the XmlCursor, with {@link #SEVERITY_ERROR}.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param cursor the XmlCursor representing the location of the error
*/
public static XmlError forCursor(String code, Object[] args, XmlCursor cursor) {
return forCursor(code, args, SEVERITY_ERROR, cursor);
}
/**
* Returns an XmlError for the given message, with the given severity, located at the XmlCursor.
*
* @param message the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param cursor the XmlCursor representing the location of the error
*/
public static XmlError forCursor(String message, int severity, XmlCursor cursor) {
return new XmlError(message, (String) null, severity, cursor);
}
/**
* Returns an XmlError for the given message, with the given severity, located at the XmlCursor.
*
* @param code the error code
* @param args the arguments to use in formatting the error message
* @param severity the severity ({@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO})
* @param cursor the XmlCursor representing the location of the error
*/
public static XmlError forCursor(String code, Object[] args, int severity, XmlCursor cursor) {
return new XmlError(code, args, severity, cursor);
}
/**
* Tries to produce a nicely formatted filename from the given string.
*/
protected static String formattedFileName(String rawString, URI base) {
if (rawString == null) {
return null;
}
URI uri;
try {
// if it looks like an absolute URI, treat it as such
uri = new URI(rawString);
// otherwise, treat it like a filename
if (!uri.isAbsolute()) {
uri = null;
}
} catch (URISyntaxException e) {
uri = null;
}
// looks like a filename; convert it to uri for relativization
if (uri == null) {
uri = new File(rawString).toURI();
}
if (base != null) {
uri = base.relativize(uri);
}
// filenames get their file: stripped off and their /'s turned into \'s (MSDOS)
if (uri.isAbsolute() ? uri.getScheme().compareToIgnoreCase("file") == 0 :
base != null && base.isAbsolute() && base.getScheme().compareToIgnoreCase("file") == 0) {
try {
return (new File(uri)).toString();
} catch (Exception ignored) {
}
}
return uri.toString();
}
/**
* Tries to format a message using the error code.
*/
public static String formattedMessage(String code, Object[] args) {
if (code == null) {
return null;
}
try {
return new MessageFormat(_bundle.getString(code), Locale.ROOT).format(args);
} catch (MissingResourceException | IllegalArgumentException e) {
String bnd = (e instanceof MissingResourceException) ? "message.missing.resource" : "message.pattern.invalid";
return new MessageFormat(_bundle.getString(bnd), Locale.ROOT).format(e.getMessage());
}
}
/**
* An error. See {@link #getSeverity}.
*/
public static final int SEVERITY_ERROR = 0;
/**
* A warning. See {@link #getSeverity}.
*/
public static final int SEVERITY_WARNING = 1;
/**
* An informational message. See {@link #getSeverity}.
*/
public static final int SEVERITY_INFO = 2;
/**
* Returns the severity. Either {@link #SEVERITY_ERROR}, {@link #SEVERITY_WARNING}, or {@link #SEVERITY_INFO}.
*/
public int getSeverity() {
return _severity;
}
/**
* Returns the error message without location information.
*/
public String getMessage() {
return _message;
}
/**
* Returns the error code or null. See {@link XmlErrorCodes}.
*/
public String getErrorCode() {
return _code;
}
/**
* Returns the URL (or other name) of the file with the error, if available.
*/
public String getSourceName() {
return _source;
}
/**
* Returns the line number of the error, if available, -1 if not.
*/
public int getLine() {
return _line;
}
/**
* Returns the column number of the error, if available, -1 if not.
*/
public int getColumn() {
return _column;
}
/**
* Returns the file character offset of the error, if available, -1 if not.
*/
public int getOffset() {
return _offset;
}
/**
* Returns a location object of the given type. XmlCursor.class and
* XmlObject.class can be passed, for example. Null if not available.
*/
public Object getLocation(Object type) {
if (type == XmlCursor.class) {
return _cursor;
}
if (type == XmlObject.class && _cursor != null) {
return _cursor.getObject();
}
return null;
}
/**
* Returns a location of the error as an {@link XmlCursor}, null if
* not available.
*/
public XmlCursor getCursorLocation() {
return (XmlCursor) getLocation(XmlCursor.class);
}
/**
* Returns a location of the error as an {@link XmlObject}, null if
* not available.
*/
public XmlObject getObjectLocation() {
return (XmlObject) getLocation(XmlObject.class);
}
/**
* Produces a standard string for the error message, complete with
* filename and location offsets if available.
*/
public String toString() {
return toString(null);
}
/**
* Produces a standard string with the error message. If a non-null
* URI is supplied, source names are relativized against the given
* URI.
*/
public String toString(URI base) {
// modified to carefully match the IDE's
// workshop.workspace.ant.AntLogger regex
// which also matches javac (davidbau)
StringBuilder sb = new StringBuilder();
String source = formattedFileName(getSourceName(), base);
if (source != null) {
sb.append(source);
int line = getLine();
if (line < 0) {
line = 0;
}
sb.append(':');
sb.append(line);
sb.append(':');
if (getColumn() > 0) {
sb.append(getColumn());
sb.append(':');
}
sb.append(" ");
}
switch (getSeverity()) {
case SEVERITY_ERROR:
sb.append("error: ");
break;
case SEVERITY_WARNING:
sb.append("warning: ");
break;
case SEVERITY_INFO:
break;
}
if (getErrorCode() != null) {
sb.append(getErrorCode()).append(": ");
}
String msg = getMessage();
sb.append(msg == null ? "<Unspecified message>" : msg);
return sb.toString();
}
public static String severityAsString(int severity) {
switch (severity) {
case SEVERITY_ERROR:
return ("error");
case SEVERITY_WARNING:
return ("warning");
case SEVERITY_INFO:
return "info";
default:
throw new IllegalArgumentException("unknown severity");
}
}
}