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