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