001: /*
002: * $Id: XMLErrorReporter.java,v 1.2 2006/04/01 06:01:49 jeffsuttor Exp $
003: */
004:
005: /*
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * [Name of File] [ver.__] [Date]
025: *
026: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
027: */
028:
029: /*
030: * The Apache Software License, Version 1.1
031: *
032: *
033: * Copyright (c) 1999-2002 The Apache Software Foundation.
034: * All rights reserved.
035: *
036: * Redistribution and use in source and binary forms, with or without
037: * modification, are permitted provided that the following conditions
038: * are met:
039: *
040: * 1. Redistributions of source code must retain the above copyright
041: * notice, this list of conditions and the following disclaimer.
042: *
043: * 2. Redistributions in binary form must reproduce the above copyright
044: * notice, this list of conditions and the following disclaimer in
045: * the documentation and/or other materials provided with the
046: * distribution.
047: *
048: * 3. The end-user documentation included with the redistribution,
049: * if any, must include the following acknowledgment:
050: * "This product includes software developed by the
051: * Apache Software Foundation (http://www.apache.org/)."
052: * Alternately, this acknowledgment may appear in the software itself,
053: * if and wherever such third-party acknowledgments normally appear.
054: *
055: * 4. The names "Xerces" and "Apache Software Foundation" must
056: * not be used to endorse or promote products derived from this
057: * software without prior written permission. For written
058: * permission, please contact apache@apache.org.
059: *
060: * 5. Products derived from this software may not be called "Apache",
061: * nor may "Apache" appear in their name, without prior written
062: * permission of the Apache Software Foundation.
063: *
064: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
065: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
066: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
067: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
068: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
069: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
070: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
071: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
072: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
073: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
074: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
075: * SUCH DAMAGE.
076: * ====================================================================
077: *
078: * This software consists of voluntary contributions made by many
079: * individuals on behalf of the Apache Software Foundation and was
080: * originally based on software copyright (c) 1999, International
081: * Business Machines, Inc., http://www.apache.org. For more
082: * information on the Apache Software Foundation, please see
083: * <http://www.apache.org/>.
084: */
085:
086: package com.sun.xml.stream;
087:
088: import java.util.Hashtable;
089: import java.util.Locale;
090:
091: import com.sun.xml.stream.xerces.util.DefaultErrorHandler;
092: import com.sun.xml.stream.xerces.util.MessageFormatter;
093: import com.sun.xml.stream.xerces.xni.XMLLocator;
094: import com.sun.xml.stream.xerces.xni.XNIException;
095: import com.sun.xml.stream.xerces.xni.parser.*;
096:
097: /**
098: * This class is a common element of all parser configurations and is
099: * used to report errors that occur. This component can be queried by
100: * parser components from the component manager using the following
101: * property ID:
102: * <pre>
103: * http://apache.org/xml/properties/internal/error-reporter
104: * </pre>
105: * <p>
106: * Errors are separated into domains that categorize a class of errors.
107: * In a parser configuration, the parser would register a
108: * <code>MessageFormatter</code> for each domain that is capable of
109: * localizing error messages and formatting them based on information
110: * about the error. Any parser component can invent new error domains
111: * and register additional message formatters to localize messages in
112: * those domains.
113: * <p>
114: * This component requires the following features and properties from the
115: * component manager that uses it:
116: * <ul>
117: * <li>http://apache.org/xml/properties/internal/error-handler</li>
118: * </ul>
119: * <p>
120: * This component can use the following features and properties but they
121: * are not required:
122: * <ul>
123: * <li>http://apache.org/xml/features/continue-after-fatal-error</li>
124: * </ul>
125: *
126: * @see MessageFormatter
127: *
128: * @author Eric Ye, IBM
129: * @author Andy Clark, IBM
130: *
131: * @version $Id: XMLErrorReporter.java,v 1.2 2006/04/01 06:01:49 jeffsuttor Exp $
132: */
133: public class XMLErrorReporter implements XMLComponent {
134:
135: //
136: // Constants
137: //
138:
139: // severity
140:
141: /**
142: * Severity: warning. Warnings represent informational messages only
143: * that should not be considered serious enough to stop parsing or
144: * indicate an error in the document's validity.
145: */
146: public static final short SEVERITY_WARNING = 0;
147:
148: /**
149: * Severity: error. Common causes of errors are document structure and/or
150: * content that that does not conform to the grammar rules specified for
151: * the document. These are typically validation errors.
152: */
153: public static final short SEVERITY_ERROR = 1;
154:
155: /**
156: * Severity: fatal error. Fatal errors are errors in the syntax of the
157: * XML document or invalid byte sequences for a given encoding. The
158: * XML 1.0 Specification mandates that errors of this type are not
159: * recoverable.
160: * <p>
161: * <strong>Note:</strong> The parser does have a "continue after fatal
162: * error" feature but it should be used with extreme caution and care.
163: */
164: public static final short SEVERITY_FATAL_ERROR = 2;
165:
166: // feature identifiers
167:
168: /** Feature identifier: continue after fatal error. */
169: protected static final String CONTINUE_AFTER_FATAL_ERROR = Constants.XERCES_FEATURE_PREFIX
170: + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
171:
172: // property identifiers
173:
174: /** Property identifier: error handler. */
175: protected static final String ERROR_HANDLER = Constants.XERCES_PROPERTY_PREFIX
176: + Constants.ERROR_HANDLER_PROPERTY;
177:
178: // recognized features and properties
179:
180: /** Recognized features. */
181: private static final String[] RECOGNIZED_FEATURES = { CONTINUE_AFTER_FATAL_ERROR, };
182:
183: /** Feature defaults. */
184: private static final Boolean[] FEATURE_DEFAULTS = { null, };
185:
186: /** Recognized properties. */
187: private static final String[] RECOGNIZED_PROPERTIES = { ERROR_HANDLER, };
188:
189: /** Property defaults. */
190: private static final Object[] PROPERTY_DEFAULTS = { null, };
191:
192: //
193: // Data
194: //
195:
196: /** The locale to be used to format error messages. */
197: protected Locale fLocale;
198:
199: /** Mapping of Message formatters for domains. */
200: protected Hashtable fMessageFormatters;
201:
202: /** Error handler. */
203: protected XMLErrorHandler fErrorHandler;
204:
205: /** Document locator. */
206: protected XMLLocator fLocator;
207:
208: /** Continue after fatal error feature. */
209: protected boolean fContinueAfterFatalError;
210:
211: /**
212: * Default error handler. This error handler is only used in the
213: * absence of a registered error handler so that errors are not
214: * "swallowed" silently. This is one of the most common "problems"
215: * reported by users of the parser.
216: */
217: protected XMLErrorHandler fDefaultErrorHandler;
218:
219: //
220: // Constructors
221: //
222:
223: /** Constructs an error reporter with a locator. */
224: public XMLErrorReporter() {
225:
226: // REVISIT: [Q] Should the locator be passed to the reportError
227: // method? Otherwise, there is no way for a parser
228: // component to store information about where an
229: // error occurred so as to report it later.
230: //
231: // An example would be to record the location of
232: // IDREFs so that, at the end of the document, if
233: // there is no associated ID declared, the error
234: // could report the location information of the
235: // reference. -Ac
236: //
237: // NOTE: I added another reportError method that allows the
238: // caller to specify the location of the error being
239: // reported. -Ac
240:
241: fMessageFormatters = new Hashtable();
242:
243: } // <init>()
244:
245: //
246: // Methods
247: //
248:
249: /**
250: * Sets the current locale.
251: *
252: * @param locale The new locale.
253: */
254: public void setLocale(Locale locale) {
255: fLocale = locale;
256: } // setLocale(Locale)
257:
258: /**
259: * Gets the current locale.
260: *
261: * @return the current Locale
262: */
263: public Locale getLocale() {
264: return fLocale;
265: } // getLocale(): Locale
266:
267: /**
268: * Sets the document locator.
269: *
270: * @param locator The locator.
271: */
272: public void setDocumentLocator(XMLLocator locator) {
273: fLocator = locator;
274: } // setDocumentLocator(XMLLocator)
275:
276: /**
277: * Registers a message formatter for the specified domain.
278: * <p>
279: * <strong>Note:</strong> Registering a message formatter for a domain
280: * when there is already a formatter registered will cause the previous
281: * formatter to be lost. This method replaces any previously registered
282: * message formatter for the specified domain.
283: *
284: * @param domain
285: * @param messageFormatter
286: */
287: public void putMessageFormatter(String domain,
288: MessageFormatter messageFormatter) {
289: fMessageFormatters.put(domain, messageFormatter);
290: } // putMessageFormatter(String,MessageFormatter)
291:
292: /**
293: * Returns the message formatter associated with the specified domain,
294: * or null if no message formatter is registered for that domain.
295: *
296: * @param domain The domain of the message formatter.
297: */
298: public MessageFormatter getMessageFormatter(String domain) {
299: return (MessageFormatter) fMessageFormatters.get(domain);
300: } // getMessageFormatter(String):MessageFormatter
301:
302: /**
303: * Removes the message formatter for the specified domain and
304: * returns the removed message formatter.
305: *
306: * @param domain The domain of the message formatter.
307: */
308: public MessageFormatter removeMessageFormatter(String domain) {
309: return (MessageFormatter) fMessageFormatters.remove(domain);
310: } // removeMessageFormatter(String):MessageFormatter
311:
312: /**
313: * Reports an error. The error message passed to the error handler
314: * is formatted for the locale by the message formatter installed
315: * for the specified error domain.
316: *
317: * @param domain The error domain.
318: * @param key The key of the error message.
319: * @param arguments The replacement arguments for the error message,
320: * if needed.
321: * @param severity The severity of the error.
322: *
323: * @see #SEVERITY_WARNING
324: * @see #SEVERITY_ERROR
325: * @see #SEVERITY_FATAL_ERROR
326: */
327: public void reportError(String domain, String key,
328: Object[] arguments, short severity) throws XNIException {
329: reportError(fLocator, domain, key, arguments, severity);
330: } // reportError(String,String,Object[],short)
331:
332: /**
333: * Reports an error at a specific location.
334: *
335: * @param location The error location.
336: * @param domain The error domain.
337: * @param key The key of the error message.
338: * @param arguments The replacement arguments for the error message,
339: * if needed.
340: * @param severity The severity of the error.
341: *
342: * @see #SEVERITY_WARNING
343: * @see #SEVERITY_ERROR
344: * @see #SEVERITY_FATAL_ERROR
345: */
346: public void reportError(XMLLocator location, String domain,
347: String key, Object[] arguments, short severity)
348: 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,
358: arguments);
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 = new XMLParseException(
377: location, message);
378:
379: // get error handler
380: XMLErrorHandler errorHandler = fErrorHandler;
381: if (errorHandler == null) {
382: if (fDefaultErrorHandler == null) {
383: fDefaultErrorHandler = new DefaultErrorHandler();
384: }
385: errorHandler = fDefaultErrorHandler;
386: }
387:
388: // call error handler
389: switch (severity) {
390: case SEVERITY_WARNING: {
391: errorHandler.warning(domain, key, parseException);
392: break;
393: }
394: case SEVERITY_ERROR: {
395: errorHandler.error(domain, key, parseException);
396: break;
397: }
398: case SEVERITY_FATAL_ERROR: {
399: errorHandler.fatalError(domain, key, parseException);
400: if (!fContinueAfterFatalError) {
401: throw parseException;
402: }
403: break;
404: }
405: }
406:
407: } // reportError(XMLLocator,String,String,Object[],short)
408:
409: //
410: // XMLComponent methods
411: //
412:
413: /**
414: * Resets the component. The component can query the component manager
415: * about any features and properties that affect the operation of the
416: * component.
417: *
418: * @param componentManager The component manager.
419: *
420: * @throws SAXException Thrown by component on initialization error.
421: * For example, if a feature or property is
422: * required for the operation of the component, the
423: * component manager may throw a
424: * SAXNotRecognizedException or a
425: * SAXNotSupportedException.
426: */
427: public void reset(XMLComponentManager componentManager)
428: throws XNIException {
429:
430: // features
431: try {
432: fContinueAfterFatalError = componentManager
433: .getFeature(CONTINUE_AFTER_FATAL_ERROR);
434: } catch (XNIException e) {
435: fContinueAfterFatalError = false;
436: }
437:
438: // properties
439: fErrorHandler = (XMLErrorHandler) componentManager
440: .getProperty(ERROR_HANDLER);
441:
442: } // reset(XMLComponentManager)
443:
444: /**
445: * Returns a list of feature identifiers that are recognized by
446: * this component. This method may return null if no features
447: * are recognized by this component.
448: */
449: public String[] getRecognizedFeatures() {
450: return (String[]) (RECOGNIZED_FEATURES.clone());
451: } // getRecognizedFeatures():String[]
452:
453: /**
454: * Sets the state of a feature. This method is called by the component
455: * manager any time after reset when a feature changes state.
456: * <p>
457: * <strong>Note:</strong> Components should silently ignore features
458: * that do not affect the operation of the component.
459: *
460: * @param featureId The feature identifier.
461: * @param state The state of the feature.
462: *
463: * @throws SAXNotRecognizedException The component should not throw
464: * this exception.
465: * @throws SAXNotSupportedException The component should not throw
466: * this exception.
467: */
468: public void setFeature(String featureId, boolean state)
469: throws XMLConfigurationException {
470:
471: //
472: // Xerces features
473: //
474:
475: if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
476: String feature = featureId
477: .substring(Constants.XERCES_FEATURE_PREFIX.length());
478: //
479: // http://apache.org/xml/features/continue-after-fatal-error
480: // Allows the parser to continue after a fatal error.
481: // Normally, a fatal error would stop the parse.
482: //
483: if (feature
484: .equals(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
485: fContinueAfterFatalError = state;
486: }
487: }
488:
489: } // setFeature(String,boolean)
490:
491: // return state of given feature or false if unsupported.
492: public boolean getFeature(String featureId)
493: throws XMLConfigurationException {
494:
495: //
496: // Xerces features
497: //
498:
499: if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
500: String feature = featureId
501: .substring(Constants.XERCES_FEATURE_PREFIX.length());
502: //
503: // http://apache.org/xml/features/continue-after-fatal-error
504: // Allows the parser to continue after a fatal error.
505: // Normally, a fatal error would stop the parse.
506: //
507: if (feature
508: .equals(Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE)) {
509: return fContinueAfterFatalError;
510: }
511: }
512: return false;
513:
514: } // setFeature(String,boolean)
515:
516: /**
517: * Returns a list of property identifiers that are recognized by
518: * this component. This method may return null if no properties
519: * are recognized by this component.
520: */
521: public String[] getRecognizedProperties() {
522: return (String[]) (RECOGNIZED_PROPERTIES.clone());
523: } // getRecognizedProperties():String[]
524:
525: /**
526: * Sets the value of a property. This method is called by the component
527: * manager any time after reset when a property changes value.
528: * <p>
529: * <strong>Note:</strong> Components should silently ignore properties
530: * that do not affect the operation of the component.
531: *
532: * @param propertyId The property identifier.
533: * @param value The value of the property.
534: *
535: * @throws SAXNotRecognizedException The component should not throw
536: * this exception.
537: * @throws SAXNotSupportedException The component should not throw
538: * this exception.
539: */
540: public void setProperty(String propertyId, Object value)
541: throws XMLConfigurationException {
542:
543: //
544: // Xerces properties
545: //
546:
547: if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
548: String property = propertyId
549: .substring(Constants.XERCES_PROPERTY_PREFIX
550: .length());
551:
552: if (property.equals(Constants.ERROR_HANDLER_PROPERTY)) {
553: fErrorHandler = (XMLErrorHandler) value;
554: }
555: }
556:
557: } // setProperty(String,Object)
558:
559: /**
560: * Returns the default state for a feature, or null if this
561: * component does not want to report a default value for this
562: * feature.
563: *
564: * @param featureId The feature identifier.
565: *
566: * @since Xerces 2.2.0
567: */
568: public Boolean getFeatureDefault(String featureId) {
569: for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
570: if (RECOGNIZED_FEATURES[i].equals(featureId)) {
571: return FEATURE_DEFAULTS[i];
572: }
573: }
574: return null;
575: } // getFeatureDefault(String):Boolean
576:
577: /**
578: * Returns the default state for a property, or null if this
579: * component does not want to report a default value for this
580: * property.
581: *
582: * @param propertyId The property identifier.
583: *
584: * @since Xerces 2.2.0
585: */
586: public Object getPropertyDefault(String propertyId) {
587: for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
588: if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
589: return PROPERTY_DEFAULTS[i];
590: }
591: }
592: return null;
593: } // getPropertyDefault(String):Object
594:
595: /**
596: * Get the internal XMLErrrorHandler.
597: */
598: public XMLErrorHandler getErrorHandler() {
599: return fErrorHandler;
600: }
601:
602: } // class XMLErrorReporter
|