001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl;
019:
020: import java.util.Hashtable;
021: import java.util.Locale;
022:
023: import org.apache.xerces.util.DefaultErrorHandler;
024: import org.apache.xerces.util.ErrorHandlerProxy;
025: import org.apache.xerces.util.MessageFormatter;
026: import org.apache.xerces.xni.XMLLocator;
027: import org.apache.xerces.xni.XNIException;
028: import org.apache.xerces.xni.parser.XMLComponent;
029: import org.apache.xerces.xni.parser.XMLComponentManager;
030: import org.apache.xerces.xni.parser.XMLConfigurationException;
031: import org.apache.xerces.xni.parser.XMLErrorHandler;
032: import org.apache.xerces.xni.parser.XMLParseException;
033: import org.xml.sax.ErrorHandler;
034:
035: /**
036: * This class is a common element of all parser configurations and is
037: * used to report errors that occur. This component can be queried by
038: * parser components from the component manager using the following
039: * property ID:
040: * <pre>
041: * http://apache.org/xml/properties/internal/error-reporter
042: * </pre>
043: * <p>
044: * Errors are separated into domains that categorize a class of errors.
045: * In a parser configuration, the parser would register a
046: * <code>MessageFormatter</code> for each domain that is capable of
047: * localizing error messages and formatting them based on information
048: * about the error. Any parser component can invent new error domains
049: * and register additional message formatters to localize messages in
050: * those domains.
051: * <p>
052: * This component requires the following features and properties from the
053: * component manager that uses it:
054: * <ul>
055: * <li>http://apache.org/xml/properties/internal/error-handler</li>
056: * </ul>
057: * <p>
058: * This component can use the following features and properties but they
059: * are not required:
060: * <ul>
061: * <li>http://apache.org/xml/features/continue-after-fatal-error</li>
062: * </ul>
063: *
064: * @xerces.internal
065: *
066: * @see MessageFormatter
067: *
068: * @author Eric Ye, IBM
069: * @author Andy Clark, IBM
070: *
071: * @version $Id: XMLErrorReporter.java 575002 2007-09-12 16:02:04Z mrglavas $
072: */
073: public class XMLErrorReporter implements XMLComponent {
074:
075: //
076: // Constants
077: //
078:
079: // severity
080:
081: /**
082: * Severity: warning. Warnings represent informational messages only
083: * that should not be considered serious enough to stop parsing or
084: * indicate an error in the document's validity.
085: */
086: public static final short SEVERITY_WARNING = 0;
087:
088: /**
089: * Severity: error. Common causes of errors are document structure and/or
090: * content that that does not conform to the grammar rules specified for
091: * the document. These are typically validation errors.
092: */
093: public static final short SEVERITY_ERROR = 1;
094:
095: /**
096: * Severity: fatal error. Fatal errors are errors in the syntax of the
097: * XML document or invalid byte sequences for a given encoding. The
098: * XML 1.0 Specification mandates that errors of this type are not
099: * recoverable.
100: * <p>
101: * <strong>Note:</strong> The parser does have a "continue after fatal
102: * error" feature but it should be used with extreme caution and care.
103: */
104: public static final short SEVERITY_FATAL_ERROR = 2;
105:
106: // feature identifiers
107:
108: /** Feature identifier: continue after fatal error. */
109: protected static final String CONTINUE_AFTER_FATAL_ERROR = Constants.XERCES_FEATURE_PREFIX
110: + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
111:
112: // property identifiers
113:
114: /** Property identifier: error handler. */
115: protected static final String ERROR_HANDLER = Constants.XERCES_PROPERTY_PREFIX
116: + Constants.ERROR_HANDLER_PROPERTY;
117:
118: // recognized features and properties
119:
120: /** Recognized features. */
121: private static final String[] RECOGNIZED_FEATURES = { CONTINUE_AFTER_FATAL_ERROR, };
122:
123: /** Feature defaults. */
124: private static final Boolean[] FEATURE_DEFAULTS = { null, };
125:
126: /** Recognized properties. */
127: private static final String[] RECOGNIZED_PROPERTIES = { ERROR_HANDLER, };
128:
129: /** Property defaults. */
130: private static final Object[] PROPERTY_DEFAULTS = { null, };
131:
132: //
133: // Data
134: //
135:
136: /** The locale to be used to format error messages. */
137: protected Locale fLocale;
138:
139: /** Mapping of Message formatters for domains. */
140: protected Hashtable fMessageFormatters;
141:
142: /** Error handler. */
143: protected XMLErrorHandler fErrorHandler;
144:
145: /** Document locator. */
146: protected XMLLocator fLocator;
147:
148: /** Continue after fatal error feature. */
149: protected boolean fContinueAfterFatalError;
150:
151: /**
152: * Default error handler. This error handler is only used in the
153: * absence of a registered error handler so that errors are not
154: * "swallowed" silently. This is one of the most common "problems"
155: * reported by users of the parser.
156: */
157: protected XMLErrorHandler fDefaultErrorHandler;
158:
159: /** A SAX proxy to the error handler contained in this error reporter. */
160: private ErrorHandler fSaxProxy = null;
161:
162: //
163: // Constructors
164: //
165:
166: /** Constructs an error reporter with a locator. */
167: public XMLErrorReporter() {
168:
169: // REVISIT: [Q] Should the locator be passed to the reportError
170: // method? Otherwise, there is no way for a parser
171: // component to store information about where an
172: // error occurred so as to report it later.
173: //
174: // An example would be to record the location of
175: // IDREFs so that, at the end of the document, if
176: // there is no associated ID declared, the error
177: // could report the location information of the
178: // reference. -Ac
179: //
180: // NOTE: I added another reportError method that allows the
181: // caller to specify the location of the error being
182: // reported. -Ac
183:
184: fMessageFormatters = new Hashtable();
185:
186: } // <init>()
187:
188: //
189: // Methods
190: //
191:
192: /**
193: * Sets the current locale.
194: *
195: * @param locale The new locale.
196: */
197: public void setLocale(Locale locale) {
198: fLocale = locale;
199: } // setLocale(Locale)
200:
201: /**
202: * Gets the current locale.
203: *
204: * @return the current Locale
205: */
206: public Locale getLocale() {
207: return fLocale;
208: } // getLocale(): Locale
209:
210: /**
211: * Sets the document locator.
212: *
213: * @param locator The locator.
214: */
215: public void setDocumentLocator(XMLLocator locator) {
216: fLocator = locator;
217: } // setDocumentLocator(XMLLocator)
218:
219: /**
220: * Registers a message formatter for the specified domain.
221: * <p>
222: * <strong>Note:</strong> Registering a message formatter for a domain
223: * when there is already a formatter registered will cause the previous
224: * formatter to be lost. This method replaces any previously registered
225: * message formatter for the specified domain.
226: *
227: * @param domain
228: * @param messageFormatter
229: */
230: public void putMessageFormatter(String domain,
231: MessageFormatter messageFormatter) {
232: fMessageFormatters.put(domain, messageFormatter);
233: } // putMessageFormatter(String,MessageFormatter)
234:
235: /**
236: * Returns the message formatter associated with the specified domain,
237: * or null if no message formatter is registered for that domain.
238: *
239: * @param domain The domain of the message formatter.
240: */
241: public MessageFormatter getMessageFormatter(String domain) {
242: return (MessageFormatter) fMessageFormatters.get(domain);
243: } // getMessageFormatter(String):MessageFormatter
244:
245: /**
246: * Removes the message formatter for the specified domain and
247: * returns the removed message formatter.
248: *
249: * @param domain The domain of the message formatter.
250: */
251: public MessageFormatter removeMessageFormatter(String domain) {
252: return (MessageFormatter) fMessageFormatters.remove(domain);
253: } // removeMessageFormatter(String):MessageFormatter
254:
255: /**
256: * Reports an error. The error message passed to the error handler
257: * is formatted for the locale by the message formatter installed
258: * for the specified error domain.
259: *
260: * @param domain The error domain.
261: * @param key The key of the error message.
262: * @param arguments The replacement arguments for the error message,
263: * if needed.
264: * @param severity The severity of the error.
265: *
266: * @see #SEVERITY_WARNING
267: * @see #SEVERITY_ERROR
268: * @see #SEVERITY_FATAL_ERROR
269: */
270: public void reportError(String domain, String key,
271: Object[] arguments, short severity) throws XNIException {
272: reportError(fLocator, domain, key, arguments, severity);
273: } // reportError(String,String,Object[],short)
274:
275: /**
276: * Reports an error. The error message passed to the error handler
277: * is formatted for the locale by the message formatter installed
278: * for the specified error domain.
279: *
280: * @param domain The error domain.
281: * @param key The key of the error message.
282: * @param arguments The replacement arguments for the error message,
283: * if needed.
284: * @param severity The severity of the error.
285: * @param exception The exception to wrap.
286: *
287: * @see #SEVERITY_WARNING
288: * @see #SEVERITY_ERROR
289: * @see #SEVERITY_FATAL_ERROR
290: */
291: public void reportError(String domain, String key,
292: Object[] arguments, short severity, Exception exception)
293: throws XNIException {
294: reportError(fLocator, domain, key, arguments, severity,
295: exception);
296: } // reportError(String,String,Object[],short,Exception)
297:
298: /**
299: * Reports an error at a specific location.
300: *
301: * @param location The error location.
302: * @param domain The error domain.
303: * @param key The key of the error message.
304: * @param arguments The replacement arguments for the error message,
305: * if needed.
306: * @param severity The severity of the error.
307: *
308: * @see #SEVERITY_WARNING
309: * @see #SEVERITY_ERROR
310: * @see #SEVERITY_FATAL_ERROR
311: */
312: public void reportError(XMLLocator location, String domain,
313: String key, Object[] arguments, short severity)
314: throws XNIException {
315: reportError(location, domain, key, arguments, severity, null);
316: } // reportError(XMLLocator,String,String,Object[],short)
317:
318: /**
319: * Reports an error at a specific location.
320: *
321: * @param location The error location.
322: * @param domain The error domain.
323: * @param key The key of the error message.
324: * @param arguments The replacement arguments for the error message,
325: * if needed.
326: * @param severity The severity of the error.
327: * @param exception The exception to wrap.
328: *
329: * @see #SEVERITY_WARNING
330: * @see #SEVERITY_ERROR
331: * @see #SEVERITY_FATAL_ERROR
332: */
333: public void reportError(XMLLocator location, String domain,
334: String key, Object[] arguments, short severity,
335: Exception exception) throws XNIException {
336:
337: // REVISIT: [Q] Should we do anything about invalid severity
338: // parameter? -Ac
339:
340: // format error message and create parse exception
341: MessageFormatter messageFormatter = getMessageFormatter(domain);
342: String message;
343: if (messageFormatter != null) {
344: message = messageFormatter.formatMessage(fLocale, key,
345: arguments);
346: } else {
347: StringBuffer str = new StringBuffer();
348: str.append(domain);
349: str.append('#');
350: str.append(key);
351: int argCount = arguments != null ? arguments.length : 0;
352: if (argCount > 0) {
353: str.append('?');
354: for (int i = 0; i < argCount; i++) {
355: str.append(arguments[i]);
356: if (i < argCount - 1) {
357: str.append('&');
358: }
359: }
360: }
361: message = str.toString();
362: }
363: XMLParseException parseException = (exception != null) ? new XMLParseException(
364: location, message, exception)
365: : new XMLParseException(location, message);
366:
367: // get error handler
368: XMLErrorHandler errorHandler = fErrorHandler;
369: if (errorHandler == null) {
370: if (fDefaultErrorHandler == null) {
371: fDefaultErrorHandler = new DefaultErrorHandler();
372: }
373: errorHandler = fDefaultErrorHandler;
374: }
375:
376: // call error handler
377: switch (severity) {
378: case SEVERITY_WARNING: {
379: errorHandler.warning(domain, key, parseException);
380: break;
381: }
382: case SEVERITY_ERROR: {
383: errorHandler.error(domain, key, parseException);
384: break;
385: }
386: case SEVERITY_FATAL_ERROR: {
387: errorHandler.fatalError(domain, key, parseException);
388: if (!fContinueAfterFatalError) {
389: throw parseException;
390: }
391: break;
392: }
393: }
394:
395: } // reportError(XMLLocator,String,String,Object[],short,Exception)
396:
397: //
398: // XMLComponent methods
399: //
400:
401: /**
402: * Resets the component. The component can query the component manager
403: * about any features and properties that affect the operation of the
404: * component.
405: *
406: * @param componentManager The component manager.
407: *
408: * @throws SAXException Thrown by component on initialization error.
409: * For example, if a feature or property is
410: * required for the operation of the component, the
411: * component manager may throw a
412: * SAXNotRecognizedException or a
413: * SAXNotSupportedException.
414: */
415: public void reset(XMLComponentManager componentManager)
416: throws XNIException {
417:
418: // features
419: try {
420: fContinueAfterFatalError = componentManager
421: .getFeature(CONTINUE_AFTER_FATAL_ERROR);
422: } catch (XNIException e) {
423: fContinueAfterFatalError = false;
424: }
425:
426: // properties
427: fErrorHandler = (XMLErrorHandler) componentManager
428: .getProperty(ERROR_HANDLER);
429:
430: } // reset(XMLComponentManager)
431:
432: /**
433: * Returns a list of feature identifiers that are recognized by
434: * this component. This method may return null if no features
435: * are recognized by this component.
436: */
437: public String[] getRecognizedFeatures() {
438: return (String[]) (RECOGNIZED_FEATURES.clone());
439: } // getRecognizedFeatures():String[]
440:
441: /**
442: * Sets the state of a feature. This method is called by the component
443: * manager any time after reset when a feature changes state.
444: * <p>
445: * <strong>Note:</strong> Components should silently ignore features
446: * that do not affect the operation of the component.
447: *
448: * @param featureId The feature identifier.
449: * @param state The state of the feature.
450: *
451: * @throws SAXNotRecognizedException The component should not throw
452: * this exception.
453: * @throws SAXNotSupportedException The component should not throw
454: * this exception.
455: */
456: public void setFeature(String featureId, boolean state)
457: throws XMLConfigurationException {
458:
459: //
460: // Xerces features
461: //
462:
463: if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
464: final int suffixLength = featureId.length()
465: - Constants.XERCES_FEATURE_PREFIX.length();
466:
467: //
468: // http://apache.org/xml/features/continue-after-fatal-error
469: // Allows the parser to continue after a fatal error.
470: // Normally, a fatal error would stop the parse.
471: //
472: if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE
473: .length()
474: && featureId
475: .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()
492: - Constants.XERCES_FEATURE_PREFIX.length();
493:
494: //
495: // http://apache.org/xml/features/continue-after-fatal-error
496: // Allows the parser to continue after a fatal error.
497: // Normally, a fatal error would stop the parse.
498: //
499: if (suffixLength == Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE
500: .length()
501: && featureId
502: .endsWith(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
503: return fContinueAfterFatalError;
504: }
505: }
506: return false;
507:
508: } // setFeature(String,boolean)
509:
510: /**
511: * Returns a list of property identifiers that are recognized by
512: * this component. This method may return null if no properties
513: * are recognized by this component.
514: */
515: public String[] getRecognizedProperties() {
516: return (String[]) (RECOGNIZED_PROPERTIES.clone());
517: } // getRecognizedProperties():String[]
518:
519: /**
520: * Sets the value of a property. This method is called by the component
521: * manager any time after reset when a property changes value.
522: * <p>
523: * <strong>Note:</strong> Components should silently ignore properties
524: * that do not affect the operation of the component.
525: *
526: * @param propertyId The property identifier.
527: * @param value The value of the property.
528: *
529: * @throws SAXNotRecognizedException The component should not throw
530: * this exception.
531: * @throws SAXNotSupportedException The component should not throw
532: * this exception.
533: */
534: public void setProperty(String propertyId, Object value)
535: throws XMLConfigurationException {
536:
537: //
538: // Xerces properties
539: //
540:
541: if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
542: final int suffixLength = propertyId.length()
543: - Constants.XERCES_PROPERTY_PREFIX.length();
544:
545: if (suffixLength == Constants.ERROR_HANDLER_PROPERTY
546: .length()
547: && propertyId
548: .endsWith(Constants.ERROR_HANDLER_PROPERTY)) {
549: fErrorHandler = (XMLErrorHandler) value;
550: }
551: }
552:
553: } // setProperty(String,Object)
554:
555: /**
556: * Returns the default state for a feature, or null if this
557: * component does not want to report a default value for this
558: * feature.
559: *
560: * @param featureId The feature identifier.
561: *
562: * @since Xerces 2.2.0
563: */
564: public Boolean getFeatureDefault(String featureId) {
565: for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
566: if (RECOGNIZED_FEATURES[i].equals(featureId)) {
567: return FEATURE_DEFAULTS[i];
568: }
569: }
570: return null;
571: } // getFeatureDefault(String):Boolean
572:
573: /**
574: * Returns the default state for a property, or null if this
575: * component does not want to report a default value for this
576: * property.
577: *
578: * @param propertyId The property identifier.
579: *
580: * @since Xerces 2.2.0
581: */
582: public Object getPropertyDefault(String propertyId) {
583: for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
584: if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
585: return PROPERTY_DEFAULTS[i];
586: }
587: }
588: return null;
589: } // getPropertyDefault(String):Object
590:
591: /**
592: * Get the internal XMLErrrorHandler.
593: */
594: public XMLErrorHandler getErrorHandler() {
595: return fErrorHandler;
596: }
597:
598: /**
599: * Gets the internal XMLErrorHandler
600: * as SAX ErrorHandler.
601: */
602: public ErrorHandler getSAXErrorHandler() {
603: if (fSaxProxy == null) {
604: fSaxProxy = new ErrorHandlerProxy() {
605: protected XMLErrorHandler getErrorHandler() {
606: return fErrorHandler;
607: }
608: };
609: }
610: return fSaxProxy;
611: }
612:
613: } // class XMLErrorReporter
|