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
64 import java.util.Hashtable;
65 import java.util.Locale;
66
67 import com.sun.org.apache.xerces.internal.util.DefaultErrorHandler;
68 import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy;
69 import com.sun.org.apache.xerces.internal.util.MessageFormatter;
70 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
71 import com.sun.org.apache.xerces.internal.xni.XNIException;
72 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
73 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
74 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
75 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
76 import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
77 import org.xml.sax.ErrorHandler;
78
79 /**
80 * This class is a common element of all parser configurations and is
81 * used to report errors that occur. This component can be queried by
82 * parser components from the component manager using the following
83 * property ID:
84 * <pre>
85 * http://apache.org/xml/properties/internal/error-reporter
86 * </pre>
87 * <p>
88 * Errors are separated into domains that categorize a class of errors.
89 * In a parser configuration, the parser would register a
90 * <code>MessageFormatter</code> for each domain that is capable of
91 * localizing error messages and formatting them based on information
92 * about the error. Any parser component can invent new error domains
93 * and register additional message formatters to localize messages in
94 * those domains.
95 * <p>
96 * This component requires the following features and properties from the
97 * component manager that uses it:
98 * <ul>
99 * <li>http://apache.org/xml/properties/internal/error-handler</li>
100 * </ul>
101 * <p>
102 * This component can use the following features and properties but they
103 * are not required:
104 * <ul>
105 * <li>http://apache.org/xml/features/continue-after-fatal-error</li>
106 * </ul>
107 *
108 * @xerces.internal
109 *
110 * @see MessageFormatter
111 *
112 * @author Eric Ye, IBM
113 * @author Andy Clark, IBM
114 *
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 //
212 // Constructors
213 //
214
215 /** Constructs an error reporter with a locator. */
216 public XMLErrorReporter() {
217
218 // REVISIT: [Q] Should the locator be passed to the reportError
219 // method? Otherwise, there is no way for a parser
220 // component to store information about where an
221 // error occurred so as to report it later.
222 //
223 // An example would be to record the location of
224 // IDREFs so that, at the end of the document, if
225 // there is no associated ID declared, the error
226 // could report the location information of the
227 // reference. -Ac
228 //
229 // NOTE: I added another reportError method that allows the
230 // caller to specify the location of the error being
231 // reported. -Ac
232
233 fMessageFormatters = new Hashtable();
234
235 } // <init>()
236
237 //
238 // Methods
239 //
240
241 /**
242 * Sets the current locale.
243 *
244 * @param locale The new locale.
245 */
246 public void setLocale(Locale locale) {
247 fLocale = locale;
248 } // setLocale(Locale)
249
250 /**
251 * Gets the current locale.
252 *
253 * @return the current Locale
254 */
255 public Locale getLocale() {
256 return fLocale ;
257 } // getLocale(): Locale
258
259 /**
260 * Sets the document locator.
261 *
262 * @param locator The locator.
263 */
264 public void setDocumentLocator(XMLLocator locator) {
265 fLocator = locator;
266 } // setDocumentLocator(XMLLocator)
267
268 /**
269 * Registers a message formatter for the specified domain.
270 * <p>
271 * <strong>Note:</strong> Registering a message formatter for a domain
272 * when there is already a formatter registered will cause the previous
273 * formatter to be lost. This method replaces any previously registered
274 * message formatter for the specified domain.
275 *
276 * @param domain
277 * @param messageFormatter
278 */
279 public void putMessageFormatter(String domain,
280 MessageFormatter messageFormatter) {
281 fMessageFormatters.put(domain, messageFormatter);
282 } // putMessageFormatter(String,MessageFormatter)
283
284 /**
285 * Returns the message formatter associated with the specified domain,
286 * or null if no message formatter is registered for that domain.
287 *
288 * @param domain The domain of the message formatter.
289 */
290 public MessageFormatter getMessageFormatter(String domain) {
291 return (MessageFormatter)fMessageFormatters.get(domain);
292 } // getMessageFormatter(String):MessageFormatter
293
294 /**
295 * Removes the message formatter for the specified domain and
296 * returns the removed message formatter.
297 *
298 * @param domain The domain of the message formatter.
299 */
300 public MessageFormatter removeMessageFormatter(String domain) {
301 return (MessageFormatter) fMessageFormatters.remove(domain);
302 } // removeMessageFormatter(String):MessageFormatter
303
304 /**
305 * Reports an error. The error message passed to the error handler
306 * is formatted for the locale by the message formatter installed
307 * for the specified error domain.
308 *
309 * @param domain The error domain.
310 * @param key The key of the error message.
311 * @param arguments The replacement arguments for the error message,
312 * if needed.
313 * @param severity The severity of the error.
314 *
315 * @see #SEVERITY_WARNING
316 * @see #SEVERITY_ERROR
317 * @see #SEVERITY_FATAL_ERROR
318 */
319 public void reportError(String domain, String key, Object[] arguments,
320 short severity) throws XNIException {
321 reportError(fLocator, domain, key, arguments, severity);
322 } // reportError(String,String,Object[],short)
323
324 /**
325 * Reports an error at a specific location.
326 *
327 * @param location The error location.
328 * @param domain The error domain.
329 * @param key The key of the error message.
330 * @param arguments The replacement arguments for the error message,
331 * if needed.
332 * @param severity The severity of the error.
333 *
334 * @see #SEVERITY_WARNING
335 * @see #SEVERITY_ERROR
336 * @see #SEVERITY_FATAL_ERROR
337 */
338 public void reportError(XMLLocator location,
339 String domain, String key, Object[] arguments,
340 short severity) throws XNIException {
341
342 // REVISIT: [Q] Should we do anything about invalid severity
343 // parameter? -Ac
344
345 // format error message and create parse exception
346 MessageFormatter messageFormatter = getMessageFormatter(domain);
347 String message;
348 if (messageFormatter != null) {
349 message = messageFormatter.formatMessage(fLocale, key, arguments);
350 }
351 else {
352 StringBuffer str = new StringBuffer();
353 str.append(domain);
354 str.append('#');
355 str.append(key);
356 int argCount = arguments != null ? arguments.length : 0;
357 if (argCount > 0) {
358 str.append('?');
359 for (int i = 0; i < argCount; i++) {
360 str.append(arguments[i]);
361 if (i < argCount -1) {
362 str.append('&');
363 }
364 }
365 }
366 message = str.toString();
367 }
368 XMLParseException parseException =
369 new XMLParseException(location, message);
370
371 // get error handler
372 XMLErrorHandler errorHandler = fErrorHandler;
373 if (errorHandler == null) {
374 if (fDefaultErrorHandler == null) {
375 fDefaultErrorHandler = new DefaultErrorHandler();
376 }
377 errorHandler = fDefaultErrorHandler;
378 }
379
380 // call error handler
381 switch (severity) {
382 case SEVERITY_WARNING: {
383 errorHandler.warning(domain, key, parseException);
384 break;
385 }
386 case SEVERITY_ERROR: {
387 errorHandler.error(domain, key, parseException);
388 break;
389 }
390 case SEVERITY_FATAL_ERROR: {
391 errorHandler.fatalError(domain, key, parseException);
392 if (!fContinueAfterFatalError) {
393 throw parseException;
394 }
395 break;
396 }
397 }
398
399 } // reportError(XMLLocator,String,String,Object[],short)
400
401 //
402 // XMLComponent methods
403 //
404
405 /**
406 * Resets the component. The component can query the component manager
407 * about any features and properties that affect the operation of the
408 * component.
409 *
410 * @param componentManager The component manager.
411 *
412 * @throws SAXException Thrown by component on initialization error.
413 * For example, if a feature or property is
414 * required for the operation of the component, the
415 * component manager may throw a
416 * SAXNotRecognizedException or a
417 * SAXNotSupportedException.
418 */
419 public void reset(XMLComponentManager componentManager)
420 throws XNIException {
421
422 // features
423 try {
424 fContinueAfterFatalError = componentManager.getFeature(CONTINUE_AFTER_FATAL_ERROR);
425 }
426 catch (XNIException e) {
427 fContinueAfterFatalError = false;
428 }
429
430 // properties
431 fErrorHandler = (XMLErrorHandler)componentManager.getProperty(ERROR_HANDLER);
432
433 } // reset(XMLComponentManager)
434
435 /**
436 * Returns a list of feature identifiers that are recognized by
437 * this component. This method may return null if no features
438 * are recognized by this component.
439 */
440 public String[] getRecognizedFeatures() {
441 return (String[])(RECOGNIZED_FEATURES.clone());
442 } // getRecognizedFeatures():String[]
443
444 /**
445 * Sets the state of a feature. This method is called by the component
446 * manager any time after reset when a feature changes state.
447 * <p>
448 * <strong>Note:</strong> Components should silently ignore features
449 * that do not affect the operation of the component.
450 *
451 * @param featureId The feature identifier.
452 * @param state The state of the feature.
453 *
454 * @throws SAXNotRecognizedException The component should not throw
455 * this exception.
456 * @throws SAXNotSupportedException The component should not throw
457 * this exception.
458 */
459 public void setFeature(String featureId, boolean state)
460 throws XMLConfigurationException {
461
462 //
463 // Xerces features
464 //
465
466 if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
467 final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
468
469 //
470 // http://apache.org/xml/features/continue-after-fatal-error
471 // Allows the parser to continue after a fatal error.
472 // Normally, a fatal error would stop the parse.
473 //
474 if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
475 featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
476 fContinueAfterFatalError = state;
477 }
478 }
479
480 } // setFeature(String,boolean)
481
482 // return state of given feature or false if unsupported.
483 public boolean getFeature(String featureId)
484 throws XMLConfigurationException {
485
486 //
487 // Xerces features
488 //
489
490 if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
491 final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
492
493 //
494 // http://apache.org/xml/features/continue-after-fatal-error
495 // Allows the parser to continue after a fatal error.
496 // Normally, a fatal error would stop the parse.
497 //
498 if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE.length() &&
499 featureId.endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
500 return fContinueAfterFatalError ;
501 }
502 }
503 return false;
504
505 } // setFeature(String,boolean)
506
507 /**
508 * Returns a list of property identifiers that are recognized by
509 * this component. This method may return null if no properties
510 * are recognized by this component.
511 */
512 public String[] getRecognizedProperties() {
513 return (String[])(RECOGNIZED_PROPERTIES.clone());
514 } // getRecognizedProperties():String[]
515
516 /**
517 * Sets the value of a property. This method is called by the component
518 * manager any time after reset when a property changes value.
519 * <p>
520 * <strong>Note:</strong> Components should silently ignore properties
521 * that do not affect the operation of the component.
522 *
523 * @param propertyId The property identifier.
524 * @param value The value of the property.
525 *
526 * @throws SAXNotRecognizedException The component should not throw
527 * this exception.
528 * @throws SAXNotSupportedException The component should not throw
529 * this exception.
530 */
531 public void setProperty(String propertyId, Object value)
532 throws XMLConfigurationException {
533
534 //
535 // Xerces properties
536 //
537
538 if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
539 final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
540
541 if (suffixLength == Constants.ERROR_HANDLER_PROPERTY.length() &&
542 propertyId.endsWith(Constants.ERROR_HANDLER_PROPERTY)) {
543 fErrorHandler = (XMLErrorHandler)value;
544 }
545 }
546
547 } // setProperty(String,Object)
548
549 /**
550 * Returns the default state for a feature, or null if this
551 * component does not want to report a default value for this
552 * feature.
553 *
554 * @param featureId The feature identifier.
555 *
556 * @since Xerces 2.2.0
557 */
558 public Boolean getFeatureDefault(String featureId) {
559 for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
560 if (RECOGNIZED_FEATURES[i].equals(featureId)) {
561 return FEATURE_DEFAULTS[i];
562 }
563 }
564 return null;
565 } // getFeatureDefault(String):Boolean
566
567 /**
568 * Returns the default state for a property, or null if this
569 * component does not want to report a default value for this
570 * property.
571 *
572 * @param propertyId The property identifier.
573 *
574 * @since Xerces 2.2.0
575 */
576 public Object getPropertyDefault(String propertyId) {
577 for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
578 if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
579 return PROPERTY_DEFAULTS[i];
580 }
581 }
582 return null;
583 } // getPropertyDefault(String):Object
584
585 /**
586 * Get the internal XMLErrrorHandler.
587 */
588 public XMLErrorHandler getErrorHandler() {
589 return fErrorHandler;
590 }
591
592
593 private ErrorHandler fSaxProxy = null;
594
595 /**
596 * Gets the internal XMLErrorHandler
597 * as SAX ErrorHandler.
598 */
599 public ErrorHandler getSAXErrorHandler() {
600 if( fSaxProxy==null )
601 fSaxProxy = new ErrorHandlerProxy() {
602 protected XMLErrorHandler getErrorHandler() {
603 return fErrorHandler;
604 }
605 };
606 return fSaxProxy;
607 }
608 } // class XMLErrorReporter