1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2004 The Apache Software Foundation.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.impl;
  58. import java.util.Hashtable;
  59. import java.util.Locale;
  60. import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
  61. import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy;
  62. import com.sun.org.apache.xerces.internal.util.MessageFormatter;
  63. import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  64. import com.sun.org.apache.xerces.internal.xni.XNIException;
  65. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  66. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  67. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  68. import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  69. import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
  70. import org.xml.sax.ErrorHandler;
  71. /**
  72. * This class is a common element of all parser configurations and is
  73. * used to report errors that occur. This component can be queried by
  74. * parser components from the component manager using the following
  75. * property ID:
  76. * <pre>
  77. * http://apache.org/xml/properties/internal/error-reporter
  78. * </pre>
  79. * <p>
  80. * Errors are separated into domains that categorize a class of errors.
  81. * In a parser configuration, the parser would register a
  82. * <code>MessageFormatter</code> for each domain that is capable of
  83. * localizing error messages and formatting them based on information
  84. * about the error. Any parser component can invent new error domains
  85. * and register additional message formatters to localize messages in
  86. * those domains.
  87. * <p>
  88. * This component requires the following features and properties from the
  89. * component manager that uses it:
  90. * <ul>
  91. * <li>http://apache.org/xml/properties/internal/error-handler</li>
  92. * </ul>
  93. * <p>
  94. * This component can use the following features and properties but they
  95. * are not required:
  96. * <ul>
  97. * <li>http://apache.org/xml/features/continue-after-fatal-error</li>
  98. * </ul>
  99. *
  100. * @see MessageFormatter
  101. *
  102. * @author Eric Ye, IBM
  103. * @author Andy Clark, IBM
  104. *
  105. * @version $Id: XMLErrorReporter.java,v 1.13 2004/01/26 17:28:12 mrglavas Exp $
  106. */
  107. public class XMLErrorReporter
  108. implements XMLComponent {
  109. //
  110. // Constants
  111. //
  112. // severity
  113. /**
  114. * Severity: warning. Warnings represent informational messages only
  115. * that should not be considered serious enough to stop parsing or
  116. * indicate an error in the document's validity.
  117. */
  118. public static final short SEVERITY_WARNING = 0;
  119. /**
  120. * Severity: error. Common causes of errors are document structure and/or
  121. * content that that does not conform to the grammar rules specified for
  122. * the document. These are typically validation errors.
  123. */
  124. public static final short SEVERITY_ERROR = 1;
  125. /**
  126. * Severity: fatal error. Fatal errors are errors in the syntax of the
  127. * XML document or invalid byte sequences for a given encoding. The
  128. * XML 1.0 Specification mandates that errors of this type are not
  129. * recoverable.
  130. * <p>
  131. * <strong>Note:</strong> The parser does have a "continue after fatal
  132. * error" feature but it should be used with extreme caution and care.
  133. */
  134. public static final short SEVERITY_FATAL_ERROR = 2;
  135. // feature identifiers
  136. /** Feature identifier: continue after fatal error. */
  137. protected static final String CONTINUE_AFTER_FATAL_ERROR =
  138. Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
  139. // property identifiers
  140. /** Property identifier: error handler. */
  141. protected static final String ERROR_HANDLER =
  142. Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;
  143. // recognized features and properties
  144. /** Recognized features. */
  145. private static final String[] RECOGNIZED_FEATURES = {
  146. CONTINUE_AFTER_FATAL_ERROR,
  147. };
  148. /** Feature defaults. */
  149. private static final Boolean[] FEATURE_DEFAULTS = {
  150. null,
  151. };
  152. /** Recognized properties. */
  153. private static final String[] RECOGNIZED_PROPERTIES = {
  154. ERROR_HANDLER,
  155. };
  156. /** Property defaults. */
  157. private static final Object[] PROPERTY_DEFAULTS = {
  158. null,
  159. };
  160. //
  161. // Data
  162. //
  163. /** The locale to be used to format error messages. */
  164. protected Locale fLocale;
  165. /** Mapping of Message formatters for domains. */
  166. protected Hashtable fMessageFormatters;
  167. /** Error handler. */
  168. protected XMLErrorHandler fErrorHandler;
  169. /** Document locator. */
  170. protected XMLLocator fLocator;
  171. /** Continue after fatal error feature. */
  172. protected boolean fContinueAfterFatalError;
  173. /**
  174. * Default error handler. This error handler is only used in the
  175. * absence of a registered error handler so that errors are not
  176. * "swallowed" silently. This is one of the most common "problems"
  177. * reported by users of the parser.
  178. */
  179. protected XMLErrorHandler fDefaultErrorHandler;
  180. //
  181. // Constructors
  182. //
  183. /** Constructs an error reporter with a locator. */
  184. public XMLErrorReporter() {
  185. // REVISIT: [Q] Should the locator be passed to the reportError
  186. // method? Otherwise, there is no way for a parser
  187. // component to store information about where an
  188. // error occurred so as to report it later.
  189. //
  190. // An example would be to record the location of
  191. // IDREFs so that, at the end of the document, if
  192. // there is no associated ID declared, the error
  193. // could report the location information of the
  194. // reference. -Ac
  195. //
  196. // NOTE: I added another reportError method that allows the
  197. // caller to specify the location of the error being
  198. // reported. -Ac
  199. fMessageFormatters = new Hashtable();
  200. } // <init>()
  201. //
  202. // Methods
  203. //
  204. /**
  205. * Sets the current locale.
  206. *
  207. * @param locale The new locale.
  208. */
  209. public void setLocale(Locale locale) {
  210. fLocale = locale;
  211. } // setLocale(Locale)
  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. * 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. * Registers a message formatter for the specified domain.
  230. * <p>
  231. * <strong>Note:</strong> Registering a message formatter for a domain
  232. * when there is already a formatter registered will cause the previous
  233. * formatter to be lost. This method replaces any previously registered
  234. * message formatter for the specified domain.
  235. *
  236. * @param domain
  237. * @param messageFormatter
  238. */
  239. public void putMessageFormatter(String domain,
  240. MessageFormatter messageFormatter) {
  241. fMessageFormatters.put(domain, messageFormatter);
  242. } // putMessageFormatter(String,MessageFormatter)
  243. /**
  244. * Returns the message formatter associated with the specified domain,
  245. * or null if no message formatter is registered for that domain.
  246. *
  247. * @param domain The domain of the message formatter.
  248. */
  249. public MessageFormatter getMessageFormatter(String domain) {
  250. return (MessageFormatter)fMessageFormatters.get(domain);
  251. } // getMessageFormatter(String):MessageFormatter
  252. /**
  253. * Removes the message formatter for the specified domain and
  254. * returns the removed message formatter.
  255. *
  256. * @param domain The domain of the message formatter.
  257. */
  258. public MessageFormatter removeMessageFormatter(String domain) {
  259. return (MessageFormatter) fMessageFormatters.remove(domain);
  260. } // removeMessageFormatter(String):MessageFormatter
  261. /**
  262. * Reports an error. The error message passed to the error handler
  263. * is formatted for the locale by the message formatter installed
  264. * for the specified error domain.
  265. *
  266. * @param domain The error domain.
  267. * @param key The key of the error message.
  268. * @param arguments The replacement arguments for the error message,
  269. * if needed.
  270. * @param severity The severity of the error.
  271. *
  272. * @see #SEVERITY_WARNING
  273. * @see #SEVERITY_ERROR
  274. * @see #SEVERITY_FATAL_ERROR
  275. */
  276. public void reportError(String domain, String key, Object[] arguments,
  277. short severity) throws XNIException {
  278. reportError(fLocator, domain, key, arguments, severity);
  279. } // reportError(String,String,Object[],short)
  280. /**
  281. * Reports an error at a specific location.
  282. *
  283. * @param location The error location.
  284. * @param domain The error domain.
  285. * @param key The key of the error message.
  286. * @param arguments The replacement arguments for the error message,
  287. * if needed.
  288. * @param severity The severity of the error.
  289. *
  290. * @see #SEVERITY_WARNING
  291. * @see #SEVERITY_ERROR
  292. * @see #SEVERITY_FATAL_ERROR
  293. */
  294. public void reportError(XMLLocator location,
  295. String domain, String key, Object[] arguments,
  296. short severity) throws XNIException {
  297. // REVISIT: [Q] Should we do anything about invalid severity
  298. // parameter? -Ac
  299. // format error message and create parse exception
  300. MessageFormatter messageFormatter = getMessageFormatter(domain);
  301. String message;
  302. if (messageFormatter != null) {
  303. message = messageFormatter.formatMessage(fLocale, key, arguments);
  304. }
  305. else {
  306. StringBuffer str = new StringBuffer();
  307. str.append(domain);
  308. str.append('#');
  309. str.append(key);
  310. int argCount = arguments != null ? arguments.length : 0;
  311. if (argCount > 0) {
  312. str.append('?');
  313. for (int i = 0; i < argCount; i++) {
  314. str.append(arguments[i]);
  315. if (i < argCount -1) {
  316. str.append('&');
  317. }
  318. }
  319. }
  320. message = str.toString();
  321. }
  322. XMLParseException parseException =
  323. new XMLParseException(location, message);
  324. // get error handler
  325. XMLErrorHandler errorHandler = fErrorHandler;
  326. if (errorHandler == null) {
  327. if (fDefaultErrorHandler == null) {
  328. fDefaultErrorHandler = new DefaultErrorHandler();
  329. }
  330. errorHandler = fDefaultErrorHandler;
  331. }
  332. // call error handler
  333. switch (severity) {
  334. case SEVERITY_WARNING: {
  335. errorHandler.warning(domain, key, parseException);
  336. break;
  337. }
  338. case SEVERITY_ERROR: {
  339. errorHandler.error(domain, key, parseException);
  340. break;
  341. }
  342. case SEVERITY_FATAL_ERROR: {
  343. errorHandler.fatalError(domain, key, parseException);
  344. if (!fContinueAfterFatalError) {
  345. throw parseException;
  346. }
  347. break;
  348. }
  349. }
  350. } // reportError(XMLLocator,String,String,Object[],short)
  351. //
  352. // XMLComponent methods
  353. //
  354. /**
  355. * Resets the component. The component can query the component manager
  356. * about any features and properties that affect the operation of the
  357. * component.
  358. *
  359. * @param componentManager The component manager.
  360. *
  361. * @throws SAXException Thrown by component on initialization error.
  362. * For example, if a feature or property is
  363. * required for the operation of the component, the
  364. * component manager may throw a
  365. * SAXNotRecognizedException or a
  366. * SAXNotSupportedException.
  367. */
  368. public void reset(XMLComponentManager componentManager)
  369. throws XNIException {
  370. // features
  371. try {
  372. fContinueAfterFatalError = componentManager.getFeature(CONTINUE_AFTER_FATAL_ERROR);
  373. }
  374. catch (XNIException e) {
  375. fContinueAfterFatalError = false;
  376. }
  377. // properties
  378. fErrorHandler = (XMLErrorHandler)componentManager.getProperty(ERROR_HANDLER);
  379. } // reset(XMLComponentManager)
  380. /**
  381. * Returns a list of feature identifiers that are recognized by
  382. * this component. This method may return null if no features
  383. * are recognized by this component.
  384. */
  385. public String[] getRecognizedFeatures() {
  386. return (String[])(RECOGNIZED_FEATURES.clone());
  387. } // getRecognizedFeatures():String[]
  388. /**
  389. * Sets the state of a feature. This method is called by the component
  390. * manager any time after reset when a feature changes state.
  391. * <p>
  392. * <strong>Note:</strong> Components should silently ignore features
  393. * that do not affect the operation of the component.
  394. *
  395. * @param featureId The feature identifier.
  396. * @param state The state of the feature.
  397. *
  398. * @throws SAXNotRecognizedException The component should not throw
  399. * this exception.
  400. * @throws SAXNotSupportedException The component should not throw
  401. * this exception.
  402. */
  403. public void setFeature(String featureId, boolean state)
  404. throws XMLConfigurationException {
  405. //
  406. // Xerces features
  407. //
  408. if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
  409. final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
  410. //
  411. // http://apache.org/xml/features/continue-after-fatal-error
  412. // Allows the parser to continue after a fatal error.
  413. // Normally, a fatal error would stop the parse.
  414. //
  415. if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
  416. featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
  417. fContinueAfterFatalError = state;
  418. }
  419. }
  420. } // setFeature(String,boolean)
  421. // return state of given feature or false if unsupported.
  422. public boolean getFeature(String featureId)
  423. throws XMLConfigurationException {
  424. //
  425. // Xerces features
  426. //
  427. if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
  428. final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
  429. //
  430. // http://apache.org/xml/features/continue-after-fatal-error
  431. // Allows the parser to continue after a fatal error.
  432. // Normally, a fatal error would stop the parse.
  433. //
  434. if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
  435. featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
  436. return fContinueAfterFatalError ;
  437. }
  438. }
  439. return false;
  440. } // setFeature(String,boolean)
  441. /**
  442. * Returns a list of property identifiers that are recognized by
  443. * this component. This method may return null if no properties
  444. * are recognized by this component.
  445. */
  446. public String[] getRecognizedProperties() {
  447. return (String[])(RECOGNIZED_PROPERTIES.clone());
  448. } // getRecognizedProperties():String[]
  449. /**
  450. * Sets the value of a property. This method is called by the component
  451. * manager any time after reset when a property changes value.
  452. * <p>
  453. * <strong>Note:</strong> Components should silently ignore properties
  454. * that do not affect the operation of the component.
  455. *
  456. * @param propertyId The property identifier.
  457. * @param value The value of the property.
  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 setProperty(String propertyId, Object value)
  465. throws XMLConfigurationException {
  466. //
  467. // Xerces properties
  468. //
  469. if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
  470. final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
  471. if (suffixLength == Constants.ERROR_HANDLER_PROPERTY.length() &&
  472. propertyId.endsWith(Constants.ERROR_HANDLER_PROPERTY)) {
  473. fErrorHandler = (XMLErrorHandler)value;
  474. }
  475. }
  476. } // setProperty(String,Object)
  477. /**
  478. * Returns the default state for a feature, or null if this
  479. * component does not want to report a default value for this
  480. * feature.
  481. *
  482. * @param featureId The feature identifier.
  483. *
  484. * @since Xerces 2.2.0
  485. */
  486. public Boolean getFeatureDefault(String featureId) {
  487. for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
  488. if (RECOGNIZED_FEATURES[i].equals(featureId)) {
  489. return FEATURE_DEFAULTS[i];
  490. }
  491. }
  492. return null;
  493. } // getFeatureDefault(String):Boolean
  494. /**
  495. * Returns the default state for a property, or null if this
  496. * component does not want to report a default value for this
  497. * property.
  498. *
  499. * @param propertyId The property identifier.
  500. *
  501. * @since Xerces 2.2.0
  502. */
  503. public Object getPropertyDefault(String propertyId) {
  504. for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
  505. if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
  506. return PROPERTY_DEFAULTS[i];
  507. }
  508. }
  509. return null;
  510. } // getPropertyDefault(String):Object
  511. /**
  512. * Get the internal XMLErrrorHandler.
  513. */
  514. public XMLErrorHandler getErrorHandler() {
  515. return fErrorHandler;
  516. }
  517. private ErrorHandler fSaxProxy = null;
  518. /**
  519. * Gets the internal XMLErrorHandler
  520. * as SAX ErrorHandler.
  521. */
  522. public ErrorHandler getSAXErrorHandler() {
  523. if( fSaxProxy==null )
  524. fSaxProxy = new ErrorHandlerProxy() {
  525. protected XMLErrorHandler getErrorHandler() {
  526. return fErrorHandler;
  527. }
  528. };
  529. return fSaxProxy;
  530. }
  531. } // class XMLErrorReporter