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