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. */
XMLErrorReporter()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      */
setLocale(Locale locale)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      */
getLocale()217     public Locale getLocale() {
218         return fLocale ;
219     } // getLocale():  Locale
220 
221     /**
222      * Sets the document locator.
223      *
224      * @param locator The locator.
225      */
setDocumentLocator(XMLLocator locator)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      */
putMessageFormatter(String domain, MessageFormatter messageFormatter)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      */
getMessageFormatter(String domain)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      */
removeMessageFormatter(String domain)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      */
reportError(String domain, String key, Object[] arguments, short severity)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      */
reportError(String domain, String key, Object[] arguments, short severity, Exception exception)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      */
reportError(XMLLocator location, String domain, String key, Object[] arguments, short severity)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      */
reportError(XMLLocator location, String domain, String key, Object[] arguments, short severity, Exception exception)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      */
reset(XMLComponentManager componentManager)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      */
getRecognizedFeatures()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      */
setFeature(String featureId, boolean state)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.
getFeature(String featureId)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      */
getRecognizedProperties()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      */
setProperty(String propertyId, Object value)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      */
getFeatureDefault(String featureId)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      */
getPropertyDefault(String propertyId)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      */
getErrorHandler()593     public XMLErrorHandler getErrorHandler() {
594         return fErrorHandler;
595     }
596 
597     /**
598      * Gets the internal XMLErrorHandler
599      * as SAX ErrorHandler.
600      */
getSAXErrorHandler()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
613