| /* |
| * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
| */ |
| /* |
| * 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. |
| */ |
| |
| package com.sun.org.apache.xerces.internal.impl; |
| import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler; |
| import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy; |
| import com.sun.org.apache.xerces.internal.util.MessageFormatter; |
| import com.sun.org.apache.xerces.internal.xni.XMLLocator; |
| import com.sun.org.apache.xerces.internal.xni.XNIException; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; |
| import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| import org.xml.sax.ErrorHandler; |
| |
| /** |
| * This class is a common element of all parser configurations and is |
| * used to report errors that occur. This component can be queried by |
| * parser components from the component manager using the following |
| * property ID: |
| * <pre> |
| * http://apache.org/xml/properties/internal/error-reporter |
| * </pre> |
| * <p> |
| * Errors are separated into domains that categorize a class of errors. |
| * In a parser configuration, the parser would register a |
| * <code>MessageFormatter</code> for each domain that is capable of |
| * localizing error messages and formatting them based on information |
| * about the error. Any parser component can invent new error domains |
| * and register additional message formatters to localize messages in |
| * those domains. |
| * <p> |
| * This component requires the following features and properties from the |
| * component manager that uses it: |
| * <ul> |
| * <li>http://apache.org/xml/properties/internal/error-handler</li> |
| * </ul> |
| * <p> |
| * This component can use the following features and properties but they |
| * are not required: |
| * <ul> |
| * <li>http://apache.org/xml/features/continue-after-fatal-error</li> |
| * </ul> |
| * |
| * @xerces.internal |
| * |
| * @see MessageFormatter |
| * |
| * @author Eric Ye, IBM |
| * @author Andy Clark, IBM |
| * |
| */ |
| public class XMLErrorReporter |
| implements XMLComponent { |
| |
| // |
| // Constants |
| // |
| |
| // severity |
| |
| /** |
| * Severity: warning. Warnings represent informational messages only |
| * that should not be considered serious enough to stop parsing or |
| * indicate an error in the document's validity. |
| */ |
| public static final short SEVERITY_WARNING = 0; |
| |
| /** |
| * Severity: error. Common causes of errors are document structure and/or |
| * content that that does not conform to the grammar rules specified for |
| * the document. These are typically validation errors. |
| */ |
| public static final short SEVERITY_ERROR = 1; |
| |
| /** |
| * Severity: fatal error. Fatal errors are errors in the syntax of the |
| * XML document or invalid byte sequences for a given encoding. The |
| * XML 1.0 Specification mandates that errors of this type are not |
| * recoverable. |
| * <p> |
| * <strong>Note:</strong> The parser does have a "continue after fatal |
| * error" feature but it should be used with extreme caution and care. |
| */ |
| public static final short SEVERITY_FATAL_ERROR = 2; |
| |
| // feature identifiers |
| |
| /** Feature identifier: continue after fatal error. */ |
| protected static final String CONTINUE_AFTER_FATAL_ERROR = |
| Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE; |
| |
| // property identifiers |
| |
| /** Property identifier: error handler. */ |
| protected static final String ERROR_HANDLER = |
| Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; |
| |
| // recognized features and properties |
| |
| /** Recognized features. */ |
| private static final String[] RECOGNIZED_FEATURES = { |
| CONTINUE_AFTER_FATAL_ERROR, |
| }; |
| |
| /** Feature defaults. */ |
| private static final Boolean[] FEATURE_DEFAULTS = { |
| null, |
| }; |
| |
| /** Recognized properties. */ |
| private static final String[] RECOGNIZED_PROPERTIES = { |
| ERROR_HANDLER, |
| }; |
| |
| /** Property defaults. */ |
| private static final Object[] PROPERTY_DEFAULTS = { |
| null, |
| }; |
| |
| // |
| // Data |
| // |
| |
| /** The locale to be used to format error messages. */ |
| protected Locale fLocale; |
| |
| /** Mapping of Message formatters for domains. */ |
| protected Map<String, MessageFormatter> fMessageFormatters; |
| |
| /** Error handler. */ |
| protected XMLErrorHandler fErrorHandler; |
| |
| /** Document locator. */ |
| protected XMLLocator fLocator; |
| |
| /** Continue after fatal error feature. */ |
| protected boolean fContinueAfterFatalError; |
| |
| /** |
| * Default error handler. This error handler is only used in the |
| * absence of a registered error handler so that errors are not |
| * "swallowed" silently. This is one of the most common "problems" |
| * reported by users of the parser. |
| */ |
| protected XMLErrorHandler fDefaultErrorHandler; |
| |
| /** A SAX proxy to the error handler contained in this error reporter. */ |
| private ErrorHandler fSaxProxy = null; |
| |
| // |
| // Constructors |
| // |
| |
| /** Constructs an error reporter with a locator. */ |
| public XMLErrorReporter() { |
| |
| // REVISIT: [Q] Should the locator be passed to the reportError |
| // method? Otherwise, there is no way for a parser |
| // component to store information about where an |
| // error occurred so as to report it later. |
| // |
| // An example would be to record the location of |
| // IDREFs so that, at the end of the document, if |
| // there is no associated ID declared, the error |
| // could report the location information of the |
| // reference. -Ac |
| // |
| // NOTE: I added another reportError method that allows the |
| // caller to specify the location of the error being |
| // reported. -Ac |
| |
| fMessageFormatters = new HashMap<>(); |
| |
| } // <init>() |
| |
| // |
| // Methods |
| // |
| |
| /** |
| * Sets the current locale. |
| * |
| * @param locale The new locale. |
| */ |
| public void setLocale(Locale locale) { |
| fLocale = locale; |
| } // setLocale(Locale) |
| |
| /** |
| * Gets the current locale. |
| * |
| * @return the current Locale |
| */ |
| public Locale getLocale() { |
| return fLocale ; |
| } // getLocale(): Locale |
| |
| /** |
| * Sets the document locator. |
| * |
| * @param locator The locator. |
| */ |
| public void setDocumentLocator(XMLLocator locator) { |
| fLocator = locator; |
| } // setDocumentLocator(XMLLocator) |
| |
| /** |
| * Registers a message formatter for the specified domain. |
| * <p> |
| * <strong>Note:</strong> Registering a message formatter for a domain |
| * when there is already a formatter registered will cause the previous |
| * formatter to be lost. This method replaces any previously registered |
| * message formatter for the specified domain. |
| * |
| * @param domain |
| * @param messageFormatter |
| */ |
| public void putMessageFormatter(String domain, |
| MessageFormatter messageFormatter) { |
| fMessageFormatters.put(domain, messageFormatter); |
| } // putMessageFormatter(String,MessageFormatter) |
| |
| /** |
| * Returns the message formatter associated with the specified domain, |
| * or null if no message formatter is registered for that domain. |
| * |
| * @param domain The domain of the message formatter. |
| */ |
| public MessageFormatter getMessageFormatter(String domain) { |
| return fMessageFormatters.get(domain); |
| } // getMessageFormatter(String):MessageFormatter |
| |
| /** |
| * Removes the message formatter for the specified domain and |
| * returns the removed message formatter. |
| * |
| * @param domain The domain of the message formatter. |
| */ |
| public MessageFormatter removeMessageFormatter(String domain) { |
| return fMessageFormatters.remove(domain); |
| } // removeMessageFormatter(String):MessageFormatter |
| |
| /** |
| * Reports an error. The error message passed to the error handler |
| * is formatted for the locale by the message formatter installed |
| * for the specified error domain. |
| * |
| * @param domain The error domain. |
| * @param key The key of the error message. |
| * @param arguments The replacement arguments for the error message, |
| * if needed. |
| * @param severity The severity of the error. |
| * @return The formatted error message. |
| * |
| * @see #SEVERITY_WARNING |
| * @see #SEVERITY_ERROR |
| * @see #SEVERITY_FATAL_ERROR |
| */ |
| public String reportError(String domain, String key, Object[] arguments, |
| short severity) throws XNIException { |
| return reportError(fLocator, domain, key, arguments, severity); |
| } // reportError(String,String,Object[],short):String |
| |
| /** |
| * Reports an error. The error message passed to the error handler |
| * is formatted for the locale by the message formatter installed |
| * for the specified error domain. |
| * |
| * @param domain The error domain. |
| * @param key The key of the error message. |
| * @param arguments The replacement arguments for the error message, |
| * if needed. |
| * @param severity The severity of the error. |
| * @param exception The exception to wrap. |
| * @return The formatted error message. |
| * |
| * @see #SEVERITY_WARNING |
| * @see #SEVERITY_ERROR |
| * @see #SEVERITY_FATAL_ERROR |
| */ |
| public String reportError(String domain, String key, Object[] arguments, |
| short severity, Exception exception) throws XNIException { |
| return reportError(fLocator, domain, key, arguments, severity, exception); |
| } // reportError(String,String,Object[],short,Exception):String |
| |
| /** |
| * Reports an error at a specific location. |
| * |
| * @param location The error location. |
| * @param domain The error domain. |
| * @param key The key of the error message. |
| * @param arguments The replacement arguments for the error message, |
| * if needed. |
| * @param severity The severity of the error. |
| * @return The formatted error message. |
| * |
| * @see #SEVERITY_WARNING |
| * @see #SEVERITY_ERROR |
| * @see #SEVERITY_FATAL_ERROR |
| */ |
| public String reportError(XMLLocator location, |
| String domain, String key, Object[] arguments, |
| short severity) throws XNIException { |
| return reportError(location, domain, key, arguments, severity, null); |
| } // reportError(XMLLocator,String,String,Object[],short):String |
| |
| /** |
| * Reports an error at a specific location. |
| * |
| * @param location The error location. |
| * @param domain The error domain. |
| * @param key The key of the error message. |
| * @param arguments The replacement arguments for the error message, |
| * if needed. |
| * @param severity The severity of the error. |
| * @param exception The exception to wrap. |
| * @return The formatted error message. |
| * |
| * @see #SEVERITY_WARNING |
| * @see #SEVERITY_ERROR |
| * @see #SEVERITY_FATAL_ERROR |
| */ |
| public String reportError(XMLLocator location, |
| String domain, String key, Object[] arguments, |
| short severity, Exception exception) throws XNIException { |
| |
| // REVISIT: [Q] Should we do anything about invalid severity |
| // parameter? -Ac |
| |
| // format error message and create parse exception |
| MessageFormatter messageFormatter = getMessageFormatter(domain); |
| String message; |
| if (messageFormatter != null) { |
| message = messageFormatter.formatMessage(fLocale, key, arguments); |
| } |
| else { |
| StringBuffer str = new StringBuffer(); |
| str.append(domain); |
| str.append('#'); |
| str.append(key); |
| int argCount = arguments != null ? arguments.length : 0; |
| if (argCount > 0) { |
| str.append('?'); |
| for (int i = 0; i < argCount; i++) { |
| str.append(arguments[i]); |
| if (i < argCount -1) { |
| str.append('&'); |
| } |
| } |
| } |
| message = str.toString(); |
| } |
| XMLParseException parseException = (exception != null) ? |
| new XMLParseException(location, message, exception) : |
| new XMLParseException(location, message); |
| |
| // get error handler |
| XMLErrorHandler errorHandler = fErrorHandler; |
| if (errorHandler == null) { |
| if (fDefaultErrorHandler == null) { |
| fDefaultErrorHandler = new DefaultErrorHandler(); |
| } |
| errorHandler = fDefaultErrorHandler; |
| } |
| |
| // call error handler |
| switch (severity) { |
| case SEVERITY_WARNING: { |
| errorHandler.warning(domain, key, parseException); |
| break; |
| } |
| case SEVERITY_ERROR: { |
| errorHandler.error(domain, key, parseException); |
| break; |
| } |
| case SEVERITY_FATAL_ERROR: { |
| errorHandler.fatalError(domain, key, parseException); |
| if (!fContinueAfterFatalError) { |
| throw parseException; |
| } |
| break; |
| } |
| } |
| return message; |
| |
| } // reportError(XMLLocator,String,String,Object[],short,Exception):String |
| |
| // |
| // XMLComponent methods |
| // |
| |
| /** |
| * Resets the component. The component can query the component manager |
| * about any features and properties that affect the operation of the |
| * component. |
| * |
| * @param componentManager The component manager. |
| * |
| * @throws SAXException Thrown by component on initialization error. |
| * For example, if a feature or property is |
| * required for the operation of the component, the |
| * component manager may throw a |
| * SAXNotRecognizedException or a |
| * SAXNotSupportedException. |
| */ |
| public void reset(XMLComponentManager componentManager) |
| throws XNIException { |
| |
| // features |
| fContinueAfterFatalError = componentManager.getFeature(CONTINUE_AFTER_FATAL_ERROR, false); |
| |
| // properties |
| fErrorHandler = (XMLErrorHandler)componentManager.getProperty(ERROR_HANDLER); |
| |
| } // reset(XMLComponentManager) |
| |
| /** |
| * Returns a list of feature identifiers that are recognized by |
| * this component. This method may return null if no features |
| * are recognized by this component. |
| */ |
| public String[] getRecognizedFeatures() { |
| return (String[])(RECOGNIZED_FEATURES.clone()); |
| } // getRecognizedFeatures():String[] |
| |
| /** |
| * Sets the state of a feature. This method is called by the component |
| * manager any time after reset when a feature changes state. |
| * <p> |
| * <strong>Note:</strong> Components should silently ignore features |
| * that do not affect the operation of the component. |
| * |
| * @param featureId The feature identifier. |
| * @param state The state of the feature. |
| * |
| * @throws SAXNotRecognizedException The component should not throw |
| * this exception. |
| * @throws SAXNotSupportedException The component should not throw |
| * this exception. |
| */ |
| public void setFeature(String featureId, boolean state) |
| throws XMLConfigurationException { |
| |
| // |
| // Xerces features |
| // |
| |
| if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { |
| final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); |
| |
| // |
| // http://apache.org/xml/features/continue-after-fatal-error |
| // Allows the parser to continue after a fatal error. |
| // Normally, a fatal error would stop the parse. |
| // |
| if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() && |
| featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) { |
| fContinueAfterFatalError = state; |
| } |
| } |
| |
| } // setFeature(String,boolean) |
| |
| // return state of given feature or false if unsupported. |
| public boolean getFeature(String featureId) |
| throws XMLConfigurationException { |
| |
| // |
| // Xerces features |
| // |
| |
| if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { |
| final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); |
| |
| // |
| // http://apache.org/xml/features/continue-after-fatal-error |
| // Allows the parser to continue after a fatal error. |
| // Normally, a fatal error would stop the parse. |
| // |
| if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() && |
| featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) { |
| return fContinueAfterFatalError ; |
| } |
| } |
| return false; |
| |
| } // setFeature(String,boolean) |
| |
| /** |
| * Returns a list of property identifiers that are recognized by |
| * this component. This method may return null if no properties |
| * are recognized by this component. |
| */ |
| public String[] getRecognizedProperties() { |
| return (String[])(RECOGNIZED_PROPERTIES.clone()); |
| } // getRecognizedProperties():String[] |
| |
| /** |
| * Sets the value of a property. This method is called by the component |
| * manager any time after reset when a property changes value. |
| * <p> |
| * <strong>Note:</strong> Components should silently ignore properties |
| * that do not affect the operation of the component. |
| * |
| * @param propertyId The property identifier. |
| * @param value The value of the property. |
| * |
| * @throws SAXNotRecognizedException The component should not throw |
| * this exception. |
| * @throws SAXNotSupportedException The component should not throw |
| * this exception. |
| */ |
| public void setProperty(String propertyId, Object value) |
| throws XMLConfigurationException { |
| |
| // |
| // Xerces properties |
| // |
| |
| if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { |
| final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); |
| |
| if (suffixLength == Constants.ERROR_HANDLER_PROPERTY.length() && |
| propertyId.endsWith(Constants.ERROR_HANDLER_PROPERTY)) { |
| fErrorHandler = (XMLErrorHandler)value; |
| } |
| } |
| |
| } // setProperty(String,Object) |
| |
| /** |
| * Returns the default state for a feature, or null if this |
| * component does not want to report a default value for this |
| * feature. |
| * |
| * @param featureId The feature identifier. |
| * |
| * @since Xerces 2.2.0 |
| */ |
| public Boolean getFeatureDefault(String featureId) { |
| for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { |
| if (RECOGNIZED_FEATURES[i].equals(featureId)) { |
| return FEATURE_DEFAULTS[i]; |
| } |
| } |
| return null; |
| } // getFeatureDefault(String):Boolean |
| |
| /** |
| * Returns the default state for a property, or null if this |
| * component does not want to report a default value for this |
| * property. |
| * |
| * @param propertyId The property identifier. |
| * |
| * @since Xerces 2.2.0 |
| */ |
| public Object getPropertyDefault(String propertyId) { |
| for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { |
| if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { |
| return PROPERTY_DEFAULTS[i]; |
| } |
| } |
| return null; |
| } // getPropertyDefault(String):Object |
| |
| /** |
| * Get the internal XMLErrrorHandler. |
| */ |
| public XMLErrorHandler getErrorHandler() { |
| return fErrorHandler; |
| } |
| |
| /** |
| * Gets the internal XMLErrorHandler |
| * as SAX ErrorHandler. |
| */ |
| public ErrorHandler getSAXErrorHandler() { |
| if (fSaxProxy == null) { |
| fSaxProxy = new ErrorHandlerProxy() { |
| protected XMLErrorHandler getErrorHandler() { |
| return fErrorHandler; |
| } |
| }; |
| } |
| return fSaxProxy; |
| } |
| |
| } // class XMLErrorReporter |