| /* 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"); |
| } |
| } |
| } |