1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Nov 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xerces.internal.impl; 23 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler; 24 import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy; 25 import com.sun.org.apache.xerces.internal.util.MessageFormatter; 26 import com.sun.org.apache.xerces.internal.xni.XMLLocator; 27 import com.sun.org.apache.xerces.internal.xni.XNIException; 28 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; 29 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 30 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 31 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; 32 import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException; 33 import java.util.HashMap; 34 import java.util.Locale; 35 import java.util.Map; 36 import org.xml.sax.ErrorHandler; 37 38 /** 39 * This class is a common element of all parser configurations and is 40 * used to report errors that occur. This component can be queried by 41 * parser components from the component manager using the following 42 * property ID: 43 * <pre> 44 * http://apache.org/xml/properties/internal/error-reporter 45 * </pre> 46 * <p> 47 * Errors are separated into domains that categorize a class of errors. 48 * In a parser configuration, the parser would register a 49 * <code>MessageFormatter</code> for each domain that is capable of 50 * localizing error messages and formatting them based on information 51 * about the error. Any parser component can invent new error domains 52 * and register additional message formatters to localize messages in 53 * those domains. 54 * <p> 55 * This component requires the following features and properties from the 56 * component manager that uses it: 57 * <ul> 58 * <li>http://apache.org/xml/properties/internal/error-handler</li> 59 * </ul> 60 * <p> 61 * This component can use the following features and properties but they 62 * are not required: 63 * <ul> 64 * <li>http://apache.org/xml/features/continue-after-fatal-error</li> 65 * </ul> 66 * 67 * @xerces.internal 68 * 69 * @see MessageFormatter 70 * 71 * @author Eric Ye, IBM 72 * @author Andy Clark, IBM 73 * 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