1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl;
  22 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
  23 import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy;
  24 import com.sun.org.apache.xerces.internal.util.MessageFormatter;
  25 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  26 import com.sun.org.apache.xerces.internal.xni.XNIException;
  27 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  28 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  29 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  30 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  31 import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
  32 import java.util.HashMap;
  33 import java.util.Locale;
  34 import java.util.Map;
  35 import org.xml.sax.ErrorHandler;
  36 
  37 /**
  38  * This class is a common element of all parser configurations and is
  39  * used to report errors that occur. This component can be queried by
  40  * parser components from the component manager using the following
  41  * property ID:
  42  * <pre>
  43  *   http://apache.org/xml/properties/internal/error-reporter
  44  * </pre>
  45  * <p>
  46  * Errors are separated into domains that categorize a class of errors.
  47  * In a parser configuration, the parser would register a
  48  * <code>MessageFormatter</code> for each domain that is capable of
  49  * localizing error messages and formatting them based on information
  50  * about the error. Any parser component can invent new error domains
  51  * and register additional message formatters to localize messages in
  52  * those domains.
  53  * <p>
  54  * This component requires the following features and properties from the
  55  * component manager that uses it:
  56  * <ul>
  57  *  <li>http://apache.org/xml/properties/internal/error-handler</li>
  58  * </ul>
  59  * <p>
  60  * This component can use the following features and properties but they
  61  * are not required:
  62  * <ul>
  63  *  <li>http://apache.org/xml/features/continue-after-fatal-error</li>
  64  * </ul>
  65  *
  66  * @xerces.internal
  67  *
  68  * @see MessageFormatter
  69  *
  70  * @author Eric Ye, IBM
  71  * @author Andy Clark, IBM
  72  *
  73  */
  74 public class XMLErrorReporter
  75     implements XMLComponent {
  76 
  77     //
  78     // Constants
  79     //
  80 
  81     // severity
  82 
  83     /**
  84      * Severity: warning. Warnings represent informational messages only
  85      * that should not be considered serious enough to stop parsing or
  86      * indicate an error in the document's validity.
  87      */
  88     public static final short SEVERITY_WARNING = 0;
  89 
  90     /**
  91      * Severity: error. Common causes of errors are document structure and/or
  92      * content that that does not conform to the grammar rules specified for
  93      * the document. These are typically validation errors.
  94      */
  95     public static final short SEVERITY_ERROR = 1;
  96 
  97     /**
  98      * Severity: fatal error. Fatal errors are errors in the syntax of the
  99      * XML document or invalid byte sequences for a given encoding. The
 100      * XML 1.0 Specification mandates that errors of this type are not
 101      * recoverable.
 102      * <p>
 103      * <strong>Note:</strong> The parser does have a "continue after fatal
 104      * error" feature but it should be used with extreme caution and care.
 105      */
 106     public static final short SEVERITY_FATAL_ERROR = 2;
 107 
 108     // feature identifiers
 109 
 110     /** Feature identifier: continue after fatal error. */
 111     protected static final String CONTINUE_AFTER_FATAL_ERROR =
 112         Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
 113 
 114     // property identifiers
 115 
 116     /** Property identifier: error handler. */
 117     protected static final String ERROR_HANDLER =
 118         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;
 119 
 120     // recognized features and properties
 121 
 122     /** Recognized features. */
 123     private static final String[] RECOGNIZED_FEATURES = {
 124         CONTINUE_AFTER_FATAL_ERROR,
 125     };
 126 
 127     /** Feature defaults. */
 128     private static final Boolean[] FEATURE_DEFAULTS = {
 129         null,
 130     };
 131 
 132     /** Recognized properties. */
 133     private static final String[] RECOGNIZED_PROPERTIES = {
 134         ERROR_HANDLER,
 135     };
 136 
 137     /** Property defaults. */
 138     private static final Object[] PROPERTY_DEFAULTS = {
 139         null,
 140     };
 141 
 142     //
 143     // Data
 144     //
 145 
 146     /** The locale to be used to format error messages. */
 147     protected Locale fLocale;
 148 
 149     /** Mapping of Message formatters for domains. */
 150     protected Map<String, MessageFormatter> fMessageFormatters;
 151 
 152     /** Error handler. */
 153     protected XMLErrorHandler fErrorHandler;
 154 
 155     /** Document locator. */
 156     protected XMLLocator fLocator;
 157 
 158     /** Continue after fatal error feature. */
 159     protected boolean fContinueAfterFatalError;
 160 
 161     /**
 162      * Default error handler. This error handler is only used in the
 163      * absence of a registered error handler so that errors are not
 164      * "swallowed" silently. This is one of the most common "problems"
 165      * reported by users of the parser.
 166      */
 167     protected XMLErrorHandler fDefaultErrorHandler;
 168 
 169     /** A SAX proxy to the error handler contained in this error reporter. */
 170     private ErrorHandler fSaxProxy = null;
 171 
 172     //
 173     // Constructors
 174     //
 175 
 176     /** Constructs an error reporter with a locator. */
 177     public XMLErrorReporter() {
 178 
 179         // REVISIT: [Q] Should the locator be passed to the reportError
 180         //              method? Otherwise, there is no way for a parser
 181         //              component to store information about where an
 182         //              error occurred so as to report it later.
 183         //
 184         //              An example would be to record the location of
 185         //              IDREFs so that, at the end of the document, if
 186         //              there is no associated ID declared, the error
 187         //              could report the location information of the
 188         //              reference. -Ac
 189         //
 190         // NOTE: I added another reportError method that allows the
 191         //       caller to specify the location of the error being
 192         //       reported. -Ac
 193 
 194         fMessageFormatters = new HashMap<>();
 195 
 196     } // <init>()
 197 
 198     //
 199     // Methods
 200     //
 201 
 202     /**
 203      * Sets the current locale.
 204      *
 205      * @param locale The new locale.
 206      */
 207     public void setLocale(Locale locale) {
 208         fLocale = locale;
 209     } // setLocale(Locale)
 210 
 211     /**
 212      * Gets the current locale.
 213      *
 214      * @return the current Locale
 215      */
 216     public Locale getLocale() {
 217         return fLocale ;
 218     } // getLocale():  Locale
 219 
 220     /**
 221      * Sets the document locator.
 222      *
 223      * @param locator The locator.
 224      */
 225     public void setDocumentLocator(XMLLocator locator) {
 226         fLocator = locator;
 227     } // setDocumentLocator(XMLLocator)
 228 
 229     /**
 230      * Registers a message formatter for the specified domain.
 231      * <p>
 232      * <strong>Note:</strong> Registering a message formatter for a domain
 233      * when there is already a formatter registered will cause the previous
 234      * formatter to be lost. This method replaces any previously registered
 235      * message formatter for the specified domain.
 236      *
 237      * @param domain
 238      * @param messageFormatter
 239      */
 240     public void putMessageFormatter(String domain,
 241                                     MessageFormatter messageFormatter) {
 242         fMessageFormatters.put(domain, messageFormatter);
 243     } // putMessageFormatter(String,MessageFormatter)
 244 
 245     /**
 246      * Returns the message formatter associated with the specified domain,
 247      * or null if no message formatter is registered for that domain.
 248      *
 249      * @param domain The domain of the message formatter.
 250      */
 251     public MessageFormatter getMessageFormatter(String domain) {
 252         return fMessageFormatters.get(domain);
 253     } // getMessageFormatter(String):MessageFormatter
 254 
 255     /**
 256      * Removes the message formatter for the specified domain and
 257      * returns the removed message formatter.
 258      *
 259      * @param domain The domain of the message formatter.
 260      */
 261     public MessageFormatter removeMessageFormatter(String domain) {
 262         return fMessageFormatters.remove(domain);
 263     } // removeMessageFormatter(String):MessageFormatter
 264 
 265     /**
 266      * Reports an error. The error message passed to the error handler
 267      * is formatted for the locale by the message formatter installed
 268      * for the specified error domain.
 269      *
 270      * @param domain    The error domain.
 271      * @param key       The key of the error message.
 272      * @param arguments The replacement arguments for the error message,
 273      *                  if needed.
 274      * @param severity  The severity of the error.
 275      * @return          The formatted error message.
 276      *
 277      * @see #SEVERITY_WARNING
 278      * @see #SEVERITY_ERROR
 279      * @see #SEVERITY_FATAL_ERROR
 280      */
 281     public String reportError(String domain, String key, Object[] arguments,
 282                             short severity) throws XNIException {
 283         return reportError(fLocator, domain, key, arguments, severity);
 284     } // reportError(String,String,Object[],short):String
 285 
 286     /**
 287      * Reports an error. The error message passed to the error handler
 288      * is formatted for the locale by the message formatter installed
 289      * for the specified error domain.
 290      *
 291      * @param domain    The error domain.
 292      * @param key       The key of the error message.
 293      * @param arguments The replacement arguments for the error message,
 294      *                  if needed.
 295      * @param severity  The severity of the error.
 296      * @param exception The exception to wrap.
 297      * @return          The formatted error message.
 298      *
 299      * @see #SEVERITY_WARNING
 300      * @see #SEVERITY_ERROR
 301      * @see #SEVERITY_FATAL_ERROR
 302      */
 303     public String reportError(String domain, String key, Object[] arguments,
 304             short severity, Exception exception) throws XNIException {
 305         return reportError(fLocator, domain, key, arguments, severity, exception);
 306     } // reportError(String,String,Object[],short,Exception):String
 307 
 308     /**
 309      * Reports an error at a specific location.
 310      *
 311      * @param location  The error location.
 312      * @param domain    The error domain.
 313      * @param key       The key of the error message.
 314      * @param arguments The replacement arguments for the error message,
 315      *                  if needed.
 316      * @param severity  The severity of the error.
 317      * @return          The formatted error message.
 318      *
 319      * @see #SEVERITY_WARNING
 320      * @see #SEVERITY_ERROR
 321      * @see #SEVERITY_FATAL_ERROR
 322      */
 323     public String reportError(XMLLocator location,
 324             String domain, String key, Object[] arguments,
 325             short severity) throws XNIException {
 326         return reportError(location, domain, key, arguments, severity, null);
 327     } // reportError(XMLLocator,String,String,Object[],short):String
 328 
 329     /**
 330      * Reports an error at a specific location.
 331      *
 332      * @param location  The error location.
 333      * @param domain    The error domain.
 334      * @param key       The key of the error message.
 335      * @param arguments The replacement arguments for the error message,
 336      *                  if needed.
 337      * @param severity  The severity of the error.
 338      * @param exception The exception to wrap.
 339      * @return          The formatted error message.
 340      *
 341      * @see #SEVERITY_WARNING
 342      * @see #SEVERITY_ERROR
 343      * @see #SEVERITY_FATAL_ERROR
 344      */
 345     public String reportError(XMLLocator location,
 346                             String domain, String key, Object[] arguments,
 347                             short severity, Exception exception) throws XNIException {
 348 
 349         // REVISIT: [Q] Should we do anything about invalid severity
 350         //              parameter? -Ac
 351 
 352         // format error message and create parse exception
 353         MessageFormatter messageFormatter = getMessageFormatter(domain);
 354         String message;
 355         if (messageFormatter != null) {
 356             message = messageFormatter.formatMessage(fLocale, key, arguments);
 357         }
 358         else {
 359             StringBuffer str = new StringBuffer();
 360             str.append(domain);
 361             str.append('#');
 362             str.append(key);
 363             int argCount = arguments != null ? arguments.length : 0;
 364             if (argCount > 0) {
 365                 str.append('?');
 366                 for (int i = 0; i < argCount; i++) {
 367                     str.append(arguments[i]);
 368                     if (i < argCount -1) {
 369                         str.append('&');
 370                     }
 371                 }
 372             }
 373             message = str.toString();
 374         }
 375         XMLParseException parseException = (exception != null) ?
 376             new XMLParseException(location, message, exception) :
 377             new XMLParseException(location, message);
 378 
 379         // get error handler
 380         XMLErrorHandler errorHandler = fErrorHandler;
 381         if (errorHandler == null) {
 382             if (fDefaultErrorHandler == null) {
 383                 fDefaultErrorHandler = new DefaultErrorHandler();
 384             }
 385             errorHandler = fDefaultErrorHandler;
 386         }
 387 
 388         // call error handler
 389         switch (severity) {
 390             case SEVERITY_WARNING: {
 391                 errorHandler.warning(domain, key, parseException);
 392                 break;
 393             }
 394             case SEVERITY_ERROR: {
 395                 errorHandler.error(domain, key, parseException);
 396                 break;
 397             }
 398             case SEVERITY_FATAL_ERROR: {
 399                 errorHandler.fatalError(domain, key, parseException);
 400                 if (!fContinueAfterFatalError) {
 401                     throw parseException;
 402                 }
 403                 break;
 404             }
 405         }
 406         return message;
 407 
 408     } // reportError(XMLLocator,String,String,Object[],short,Exception):String
 409 
 410     //
 411     // XMLComponent methods
 412     //
 413 
 414     /**
 415      * Resets the component. The component can query the component manager
 416      * about any features and properties that affect the operation of the
 417      * component.
 418      *
 419      * @param componentManager The component manager.
 420      *
 421      * @throws SAXException Thrown by component on initialization error.
 422      *                      For example, if a feature or property is
 423      *                      required for the operation of the component, the
 424      *                      component manager may throw a
 425      *                      SAXNotRecognizedException or a
 426      *                      SAXNotSupportedException.
 427      */
 428     public void reset(XMLComponentManager componentManager)
 429         throws XNIException {
 430 
 431         // features
 432         fContinueAfterFatalError = componentManager.getFeature(CONTINUE_AFTER_FATAL_ERROR, false);
 433 
 434         // properties
 435         fErrorHandler = (XMLErrorHandler)componentManager.getProperty(ERROR_HANDLER);
 436 
 437     } // reset(XMLComponentManager)
 438 
 439     /**
 440      * Returns a list of feature identifiers that are recognized by
 441      * this component. This method may return null if no features
 442      * are recognized by this component.
 443      */
 444     public String[] getRecognizedFeatures() {
 445         return (String[])(RECOGNIZED_FEATURES.clone());
 446     } // getRecognizedFeatures():String[]
 447 
 448     /**
 449      * Sets the state of a feature. This method is called by the component
 450      * manager any time after reset when a feature changes state.
 451      * <p>
 452      * <strong>Note:</strong> Components should silently ignore features
 453      * that do not affect the operation of the component.
 454      *
 455      * @param featureId The feature identifier.
 456      * @param state     The state of the feature.
 457      *
 458      * @throws SAXNotRecognizedException The component should not throw
 459      *                                   this exception.
 460      * @throws SAXNotSupportedException The component should not throw
 461      *                                  this exception.
 462      */
 463     public void setFeature(String featureId, boolean state)
 464         throws XMLConfigurationException {
 465 
 466         //
 467         // Xerces features
 468         //
 469 
 470         if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
 471             final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
 472 
 473             //
 474             // http://apache.org/xml/features/continue-after-fatal-error
 475             //   Allows the parser to continue after a fatal error.
 476             //   Normally, a fatal error would stop the parse.
 477             //
 478             if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
 479                 featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
 480                 fContinueAfterFatalError = state;
 481             }
 482         }
 483 
 484     } // setFeature(String,boolean)
 485 
 486     // return state of given feature or false if unsupported.
 487     public boolean getFeature(String featureId)
 488         throws XMLConfigurationException {
 489 
 490         //
 491         // Xerces features
 492         //
 493 
 494         if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
 495                 final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
 496 
 497             //
 498             // http://apache.org/xml/features/continue-after-fatal-error
 499             //   Allows the parser to continue after a fatal error.
 500             //   Normally, a fatal error would stop the parse.
 501             //
 502             if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
 503                 featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
 504                 return fContinueAfterFatalError ;
 505             }
 506         }
 507         return false;
 508 
 509     } // setFeature(String,boolean)
 510 
 511     /**
 512      * Returns a list of property identifiers that are recognized by
 513      * this component. This method may return null if no properties
 514      * are recognized by this component.
 515      */
 516     public String[] getRecognizedProperties() {
 517         return (String[])(RECOGNIZED_PROPERTIES.clone());
 518     } // getRecognizedProperties():String[]
 519 
 520     /**
 521      * Sets the value of a property. This method is called by the component
 522      * manager any time after reset when a property changes value.
 523      * <p>
 524      * <strong>Note:</strong> Components should silently ignore properties
 525      * that do not affect the operation of the component.
 526      *
 527      * @param propertyId The property identifier.
 528      * @param value      The value of the property.
 529      *
 530      * @throws SAXNotRecognizedException The component should not throw
 531      *                                   this exception.
 532      * @throws SAXNotSupportedException The component should not throw
 533      *                                  this exception.
 534      */
 535     public void setProperty(String propertyId, Object value)
 536         throws XMLConfigurationException {
 537 
 538         //
 539         // Xerces properties
 540         //
 541 
 542         if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
 543             final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
 544 
 545             if (suffixLength == Constants.ERROR_HANDLER_PROPERTY.length() &&
 546                 propertyId.endsWith(Constants.ERROR_HANDLER_PROPERTY)) {
 547                 fErrorHandler = (XMLErrorHandler)value;
 548             }
 549         }
 550 
 551     } // setProperty(String,Object)
 552 
 553     /**
 554      * Returns the default state for a feature, or null if this
 555      * component does not want to report a default value for this
 556      * feature.
 557      *
 558      * @param featureId The feature identifier.
 559      *
 560      * @since Xerces 2.2.0
 561      */
 562     public Boolean getFeatureDefault(String featureId) {
 563         for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
 564             if (RECOGNIZED_FEATURES[i].equals(featureId)) {
 565                 return FEATURE_DEFAULTS[i];
 566             }
 567         }
 568         return null;
 569     } // getFeatureDefault(String):Boolean
 570 
 571     /**
 572      * Returns the default state for a property, or null if this
 573      * component does not want to report a default value for this
 574      * property.
 575      *
 576      * @param propertyId The property identifier.
 577      *
 578      * @since Xerces 2.2.0
 579      */
 580     public Object getPropertyDefault(String propertyId) {
 581         for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
 582             if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
 583                 return PROPERTY_DEFAULTS[i];
 584             }
 585         }
 586         return null;
 587     } // getPropertyDefault(String):Object
 588 
 589     /**
 590      * Get the internal XMLErrrorHandler.
 591      */
 592     public XMLErrorHandler getErrorHandler() {
 593         return fErrorHandler;
 594     }
 595 
 596     /**
 597      * Gets the internal XMLErrorHandler
 598      * as SAX ErrorHandler.
 599      */
 600     public ErrorHandler getSAXErrorHandler() {
 601         if (fSaxProxy == null) {
 602             fSaxProxy = new ErrorHandlerProxy() {
 603                 protected XMLErrorHandler getErrorHandler() {
 604                     return fErrorHandler;
 605                 }
 606             };
 607         }
 608         return fSaxProxy;
 609     }
 610 
 611 } // class XMLErrorReporter