001: /* SAXHandler.java
002:
003: {{IS_NOTE
004:
005: Purpose:
006: Description:
007: History:
008: 2001/10/25 12:34:46, Create, Tom M. Yeh.
009: }}IS_NOTE
010:
011: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
012:
013: {{IS_RIGHT
014: This program is distributed under GPL Version 2.0 in the hope that
015: it will be useful, but WITHOUT ANY WARRANTY.
016: }}IS_RIGHT
017: */
018: package org.zkoss.idom.input;
019:
020: import java.util.Stack;
021: import java.util.List;
022: import java.util.LinkedList;
023: import java.util.Map;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.io.IOException;
027: import java.net.URL;
028:
029: import javax.xml.parsers.SAXParser;
030:
031: import org.xml.sax.Locator;
032: import org.xml.sax.Attributes;
033: import org.xml.sax.SAXException;
034: import org.xml.sax.SAXParseException;
035: import org.xml.sax.InputSource;
036: import org.xml.sax.EntityResolver;
037: import org.xml.sax.ErrorHandler;
038: import org.xml.sax.ext.LexicalHandler;
039: import org.xml.sax.ext.DeclHandler;
040: import org.xml.sax.helpers.DefaultHandler;
041: import org.xml.sax.XMLReader;
042:
043: import org.zkoss.lang.D;
044: import org.zkoss.lang.Objects;
045: import org.zkoss.util.logging.Log;
046: import org.zkoss.util.resource.Locators;
047: import org.zkoss.idom.util.SimpleLocator;
048: import org.zkoss.idom.*;
049:
050: /**
051: * The SAX Hanlder.
052: * It implements ContentHandler, LexicalHandler and DeclHandler.
053: * It is the caller's job to set up this handler properly if required.
054: *
055: * <p>This class doesn't depend on SAXBuilder, so it can be used in any
056: * other place, e.g., javax.xml.transform.sax.SAXResult.
057: *
058: * @author tomyeh
059: * @see SAXBuilder
060: */
061: public class SAXHandler extends DefaultHandler implements
062: LexicalHandler, DeclHandler {
063: private static final Log log = Log.lookup(SAXHandler.class);
064:
065: /** The iDOM factory. */
066: protected IDOMFactory _factory;
067:
068: //-- options --//
069: /** Whether to ignore ignorable whitespace */
070: private boolean _ignoreWhitespaces = false;
071: /** Whether expansion of entities should occur */
072: private boolean _expandEntities = true;
073: /** Whether to convert CDATA to Text and coalesce them. */
074: private boolean _coalescing = false;
075: /** Whether to ignore comments. */
076: private boolean _ignoreComments = false;
077: /** The error handler. */
078: private ErrorHandler _errHandler = null;
079: /** The entity resolver. */
080: private EntityResolver _resolver = null;
081:
082: /** The Document being created. */
083: protected Document _doc = null;
084: /** Locator. */
085: protected Locator _loc = null;
086: /** Indicator of whether we are in a DTD. */
087: protected boolean _inDTD = false;
088: /** Indicator of whether we are in a CDATA. */
089: protected boolean _inCData = false;
090: /** The Group stack. The top one is the current group being processed. */
091: protected Stack _stack = null;
092: /** The namespaces in between startPrefixMapping and endPrefixMapping. */
093: protected List _declNamespaces = null;
094: /** Temporary holder for the internal subset. */
095: private StringBuffer _internSubset = null;
096: /** Whether it is in internal subset. */
097: private boolean _inInternSubset = false;
098:
099: /**
100: * Constructor.
101: *
102: * @param factory the iDOM factory; null for DefaultIDOMFactory.
103: */
104: public SAXHandler(IDOMFactory factory) {
105: _factory = factory != null ? factory : new DefaultIDOMFactory();
106: }
107:
108: /**
109: * Constructor.
110: */
111: public SAXHandler() {
112: _factory = new DefaultIDOMFactory();
113: }
114:
115: //-- options --//
116: /**
117: * Tests whether to ignore whitespaces in element content.
118: */
119: public final boolean isIgnoringElementContentWhitespace() {
120: return _ignoreWhitespaces;
121: }
122:
123: /**
124: * Sets whether the parser should elminate whitespace in
125: * element content. They are known as "ignorable whitespace".
126: * Only whitespace which is contained within element content that has
127: * an element only content model will be eliminated (see XML Rec 2.10).
128: *
129: * <p>For this setting to take effect requires that validation be turned on.
130: *
131: * <p>Default: false.
132: *
133: * @param ignore Whether to ignore whitespaces in element content.
134: */
135: public final void setIgnoringElementContentWhitespace(boolean ignore) {
136: _ignoreWhitespaces = ignore;
137: }
138:
139: /**
140: * Tests whether to expand entity reference nodes.
141: */
142: public final boolean isExpandEntityReferences() {
143: return _expandEntities;
144: }
145:
146: /**
147: * Sets whether to expand entities during parsing.
148: * A true means to expand entities as normal content. A false means to
149: * leave entities unexpanded as <code>EntityReference</code> objects.
150: *
151: * <p>Default: true.
152: *
153: * @param expand whether entity expansion should occur.
154: */
155: public final void setExpandEntityReferences(boolean expand) {
156: _expandEntities = expand;
157: }
158:
159: /**
160: * Indicates whether or not the factory is configured to produce parsers
161: * which converts CDATA to Text and appends it to the adjacent (if any)
162: * Text node.
163: *
164: * <p>Default: false.
165: *
166: * @return true if the factory is configured to produce parsers which
167: * converts CDATA nodes to Text nodes
168: * and appends it to the adjacent (if any) Text node; false otherwise.
169: */
170: public final boolean isCoalescing() {
171: return _coalescing;
172: }
173:
174: /**
175: * Specifies that the parser produced by this code will convert
176: * CDATA to Text and append it to the adjacent (if any) text.
177: *
178: * <p>Default: false.
179: */
180: public final void setCoalescing(boolean coalescing) {
181: _coalescing = coalescing;
182: }
183:
184: /**
185: * Indicates whether or not the factory is configured to produce parsers
186: * which ignores comments.
187: *
188: * <p>Default: false.
189: *
190: * @return true if the factory is configured to produce parsers
191: * which ignores comments; false otherwise.
192: */
193: public final boolean isIgnoringComments() {
194: return _ignoreComments;
195: }
196:
197: /**
198: * Specifies that the parser produced by this code will ignore comments.
199: *
200: * <p>Default: false.
201: */
202: public final void setIgnoringComments(boolean ignoreComments) {
203: _ignoreComments = ignoreComments;
204: }
205:
206: /**
207: * Specifies the org.xml.sax.ErrorHandler to be used to report errors
208: * present in the XML document to be parsed.
209: * <p>Default: null -- to use the default imple-mentation and behavior.
210: */
211: public final void setErrorHandler(ErrorHandler eh) {
212: _errHandler = eh;
213: }
214:
215: /**
216: * Gets the org.xml.sax.ErrorHandler.
217: *
218: * @return the error handler; null to use the default implementation
219: */
220: public final ErrorHandler getErrorHandler() {
221: return _errHandler;
222: }
223:
224: /**
225: * Specifies the org.xml.sax.EntityResolver to be used to resolve
226: * entities present in the XML docu-ment to be parsed.
227: * <p>Default: null -- to use the default implementation and behavior.
228: */
229: public final void setEntityResolver(org.xml.sax.EntityResolver er) {
230: _resolver = er;
231: }
232:
233: /**
234: * Gets the org.xml.sax.EntityResolver.
235: *
236: * @return the enity resolverr; null to use the default implementation
237: */
238: public final EntityResolver getEntityResolver() {
239: return _resolver;
240: }
241:
242: //-- Extra utilities for the caller to use --//
243: /**
244: * Gets the document being created.
245: * Called to retrieve the iDOM tree after parsed.
246: */
247: public final Document getDocument() {
248: return _doc;
249: }
250:
251: /**
252: * Gets the iDOM factory. Null for DefaultIDOMFactory.THE.
253: */
254: public final IDOMFactory getIDOMFactory() {
255: return _factory;
256: }
257:
258: /**
259: * Sets the iDOM factory. Null for DefaultIDOMFactory.THE.
260: */
261: public final void setIDOMFactory(IDOMFactory factory) {
262: _factory = factory;
263: }
264:
265: //-- protected utilities --//
266: /**
267: * Attaches the locator to the item.
268: */
269: protected final void attachLocator(Item vtx) {
270: if (_loc != null)
271: vtx.setLocator(new SimpleLocator(_loc));
272: }
273:
274: /** Returns the top group, or null if not available.
275: */
276: protected final Group getTopGroup() {
277: return _stack.isEmpty() ? null : (Group) _stack.peek();
278: }
279:
280: /**
281: * Adds the item to the current group; also attach the locator.
282: */
283: protected final void addToCurrentGroup(Item vtx) {
284: attachLocator(vtx);
285: ((Group) _stack.peek()).getChildren().add(vtx);
286: }
287:
288: /**
289: * Adds a new group to the current group as a child,
290: * and pushes the new group to be the new current group.
291: */
292: protected final void pushGroup(Group group) {
293: if (_stack.isEmpty())
294: assert (group instanceof Document);
295: else
296: addToCurrentGroup(group);
297:
298: _stack.push(group);
299: }
300:
301: /**
302: * Pops out the current group, and the one under it becomes the
303: * new current group.
304: */
305: protected final void popGroup() {
306: ((Group) _stack.pop()).coalesce(false);
307: }
308:
309: //-- org.xml.sax.ext.DeclHandler --//
310: public void externalEntityDecl(String name, String pubId,
311: String sysId) throws SAXException {
312: if (D.ON && log.finerable())
313: log.finer("externalEntityDecl: " + name + " p:" + pubId
314: + " s:" + sysId);
315:
316: if (!_inInternSubset)
317: return;
318:
319: _internSubset.append(" <!ENTITY ").append(name);
320: if (pubId != null)
321: _internSubset.append(" PUBLIC \"").append(pubId).append(
322: "\" ");
323: if (sysId != null)
324: _internSubset.append(" SYSTEM \"").append(sysId).append(
325: "\" ");
326: _internSubset.append(">\n");
327: }
328:
329: public void internalEntityDecl(String name, String value)
330: throws SAXException {
331: if (D.ON && log.finerable())
332: log.finer("internalEntityDecl: " + name + '=' + value);
333:
334: if (!_inInternSubset)
335: return;
336:
337: _internSubset.append(" <!ENTITY ").append(name).append(" \"")
338: .append(value).append("\">\n");
339: }
340:
341: public void attributeDecl(String eName, String aName, String type,
342: String valueDefault, String value) throws SAXException {
343: if (!_inInternSubset)
344: return;
345:
346: _internSubset.append(" <!ATTLIST ").append(eName).append(' ')
347: .append(aName).append(' ').append(type).append(' ');
348: if (valueDefault != null) {
349: _internSubset.append(valueDefault);
350: } else {
351: _internSubset.append('"').append(value).append('"');
352: }
353: if ((valueDefault != null) && (valueDefault.equals("#FIXED"))) {
354: _internSubset.append(" \"").append(value).append('"');
355: }
356: _internSubset.append(">\n");
357: }
358:
359: public void elementDecl(String name, String model)
360: throws SAXException {
361: if (!_inInternSubset)
362: return;
363:
364: _internSubset.append(" <!ELEMENT ").append(name).append(' ')
365: .append(model).append(">\n");
366: }
367:
368: //-- org.xml.sax.ext.LexicalHandler --//
369: public void startDTD(String name, String pubId, String sysId)
370: throws SAXException {
371: if (D.ON && log.finerable())
372: log.finer("start DTD: " + name + " p:" + pubId + " s:"
373: + sysId);
374:
375: addToCurrentGroup(_factory.newDocType(name, pubId, sysId));
376: _inDTD = true;
377: _internSubset = new StringBuffer(); //TY: start use it
378: _inInternSubset = true;
379: }
380:
381: //NOTE: xerces does not invoke endDTD if nothing in there
382: public void endDTD() throws SAXException {
383: if (D.ON && log.finerable())
384: log.finer("end DTD: \"" + _internSubset + '"');
385:
386: _doc.getDocType().setInternalSubset(_internSubset.toString());
387: _inDTD = false;
388: _internSubset = null; //TY: no longer used
389: _inInternSubset = false;
390: }
391:
392: public void startEntity(String name) throws SAXException {
393: if (D.ON && log.finerable())
394: log.finer("startEntity: " + name);
395:
396: //A "[dtd]" entity indicates the beginning of the external subset
397: if (name.equals("[dtd]")) {
398: _inInternSubset = false;
399: return;
400: }
401:
402: if (!isExpandEntityReferences() && !_inDTD
403: && !entityToSkip(name)) {
404: pushGroup(_factory.newEntityRef(name));
405: }
406: }
407:
408: /** Tests whether the name is something that always expanded. */
409: private boolean entityToSkip(String name) {
410: //To speed up the performance, we don't compare all strings
411: //Rather, we use something similar to finite-state machine.
412: switch (name.charAt(0)) {
413: case 'a':
414: return name.equals("amp") || name.equals("apos");
415: case 'g':
416: return name.equals("gt");
417: case 'l':
418: return name.equals("lt");
419: case 'q':
420: return name.equals("quot");
421: }
422: return false;
423: }
424:
425: public void endEntity(String name) throws SAXException {
426: if (D.ON && log.finerable())
427: log.finer("endEntity: " + name);
428:
429: if (name.equals("[dtd]")) {
430: _inInternSubset = false;
431: return;
432: }
433: if (!isExpandEntityReferences() && !_inDTD
434: && !entityToSkip(name)) {
435: popGroup();
436: }
437: }
438:
439: public void startCDATA() throws SAXException {
440: _inCData = true;
441: }
442:
443: public void endCDATA() throws SAXException {
444: _inCData = false;
445: }
446:
447: public void comment(char[] ch, int start, int length)
448: throws SAXException {
449: if (length == 0 || isIgnoringComments())
450: return; //ignore zero length
451:
452: String data = new String(ch, start, length);
453: if (_inDTD && _inInternSubset && !isExpandEntityReferences()) {
454: _internSubset.append(" <!--").append(data).append("-->\n");
455: }
456: if (!_inDTD && data.length() != 0) {
457: addToCurrentGroup(_factory.newComment(data));
458: }
459: }
460:
461: //-- org.xml.sax.ContentHandler --//
462: public void startDocument() throws SAXException {
463: _declNamespaces = new LinkedList();
464: _stack = new Stack();
465:
466: _doc = _factory.newDocument(null, null);
467: pushGroup(_doc);
468: }
469:
470: public void endDocument() throws SAXException {
471: popGroup();
472: assert (_stack.isEmpty());
473:
474: _stack = null;
475: _loc = null;
476: _declNamespaces = null;
477: }
478:
479: public void setDocumentLocator(Locator locator) {
480: _loc = locator;
481: }
482:
483: public void processingInstruction(String target, String data)
484: throws SAXException {
485: addToCurrentGroup(_factory.newProcessingInstruction(target,
486: data));
487: }
488:
489: public void startPrefixMapping(String prefix, String uri)
490: throws SAXException {
491: if (D.ON && log.finerable())
492: log.finer("start prefix: " + prefix + ", " + uri
493: + SimpleLocator.toString(_loc));
494:
495: final Namespace ns = Namespace.getSpecial(prefix);
496: if (ns == null || !ns.getURI().equals(uri))
497: _declNamespaces.add(0, new Namespace(prefix, uri));
498: //TY: add at the head to speed up the searching
499: }
500:
501: public void endPrefixMapping(String prefix) throws SAXException {
502: if (D.ON && log.finerable())
503: log.finer("end prefix: " + prefix
504: + SimpleLocator.toString(_loc));
505:
506: for (Iterator itr = _declNamespaces.iterator(); itr.hasNext();) {
507: final Namespace ns = (Namespace) itr.next();
508: if (prefix.equals(ns.getPrefix())) {
509: itr.remove();
510: break;
511: }
512: }
513: }
514:
515: public void startElement(String nsURI, String lname, String tname,
516: Attributes attrs) throws SAXException {
517: if (D.ON && log.finerable())
518: log.finer("start element: nsURI=\"" + nsURI + "\", lname="
519: + lname + ", tname=" + tname + " attr#="
520: + attrs.getLength() + SimpleLocator.toString(_loc));
521:
522: //create the element
523: if (tname == null || tname.length() == 0) //just in case
524: tname = lname;
525: final Element element = newElement(nsURI, tname);
526: //note: in crimson, lname might be empty, so...
527:
528: //Handle attributes
529: for (int j = 0, len = attrs.getLength(); j < len; j++) {
530: String attQname = attrs.getQName(j);
531: String attLname = attrs.getLocalName(j);
532:
533: if (attQname == null || attQname.length() == 0) //just in case
534: attQname = attLname;
535:
536: final Attribute attr;
537: int kp = attQname.indexOf(':');
538: if (kp >= 0) {
539: final String prefix = attQname.substring(0, kp);
540: final Namespace ns = element.getNamespace(prefix);
541: if (ns == null)
542: throw new SAXException(
543: "Unknown prefix: "
544: + prefix
545: + " at "
546: + _loc
547: + "\nDo you forget to turn on namespace-aware");
548: attr = _factory.newAttribute(ns, attQname
549: .substring(kp + 1), attrs.getValue(j));
550: //if prefix, attLname might be empty so use attQname
551: } else {
552: attr = _factory
553: .newAttribute(attLname == null
554: || attLname.length() == 0 ? attQname
555: : attLname, //crimson might use empty for AttLname
556: attrs.getValue(j));
557: }
558: attachLocator(attr);
559: element.getAttributeItems().add(attr);
560: //Don't use setAttribute, to can detect replicated attributes
561: }
562: }
563:
564: private Element newElement(String nsURI, String tname)
565: throws SAXException {
566: if (nsURI == null)
567: nsURI = "";
568:
569: final int j = tname.indexOf(':');
570: final String prefix = j >= 0 ? tname.substring(0, j) : "";
571: final String lname = j >= 0 ? tname.substring(j + 1) : tname;
572:
573: Namespace ns;
574: final Group parent = getTopGroup();
575: if (parent instanceof Element) {
576: ns = ((Element) parent).getNamespace(prefix);
577: if (ns != null && !ns.getURI().equals(nsURI)) //override
578: ns = null; //create a no-namespace element first
579: } else {
580: ns = Namespace.getSpecial(prefix);
581: }
582:
583: if (ns == null) {
584: if (_declNamespaces.size() > 0) {
585: for (Iterator it = _declNamespaces.iterator(); it
586: .hasNext();) {
587: final Namespace n = (Namespace) it.next();
588: if (n.getPrefix().equals(prefix)
589: && n.getURI().equals(nsURI)) {
590: if (D.ON && log.finerable())
591: log
592: .finer("Namespace found in _declNamespaces: "
593: + n);
594: ns = n;
595: break; //found
596: }
597: }
598: }
599: if (ns == null && nsURI.length() > 0) {
600: if (D.ON && log.finerable())
601: log.finer("Create namespace: " + prefix + " "
602: + nsURI);
603: ns = new Namespace(prefix, nsURI);
604: }
605: }
606:
607: final Element element = ns != null ? _factory.newElement(ns,
608: lname) : _factory.newElement(lname);
609:
610: //add to element's add. namespaces
611: if (_declNamespaces.size() > 0) {
612: for (Iterator it = _declNamespaces.iterator(); it.hasNext();)
613: element.addDeclaredNamespace((Namespace) it.next());
614: _declNamespaces.clear();
615: }
616:
617: pushGroup(element);
618: return element;
619: }
620:
621: public void endElement(String nsURI, String lname, String tname)
622: throws SAXException {
623: if (D.ON && log.finerable())
624: log.finer("end element: " + nsURI + ", " + lname + ", "
625: + tname);
626:
627: popGroup();
628: }
629:
630: public void characters(char[] ch, int start, int length)
631: throws SAXException {
632: if (length == 0)
633: return; //ignore zero length
634:
635: //Note: Element's add will coalesce consecutive CDATA or Text
636: final String data = new String(ch, start, length);
637: if (getTopGroup() instanceof Document) {
638: if (data.trim().length() > 0)
639: throw new SAXException(
640: "Adding non-empty text to Document: " + data);
641: return; //Under transforming, it is possible to have this case
642: }
643:
644: if (_inCData && !isCoalescing())
645: addToCurrentGroup(_factory.newCData(data));
646: else
647: addToCurrentGroup(_factory.newText(data));
648: }
649:
650: public void ignorableWhitespace(char[] ch, int start, int length)
651: throws SAXException {
652: if (length == 0 || isIgnoringElementContentWhitespace())
653: return;
654:
655: addToCurrentGroup(_factory
656: .newText(new String(ch, start, length)));
657: }
658:
659: //-- org.xml.sax.DTDHandler --//
660: public void notationDecl(String name, String publicID,
661: String systemID) throws SAXException {
662: if (!_inInternSubset)
663: return;
664:
665: _internSubset.append(" <!NOTATION ").append(name)
666: .append(" \"").append(systemID).append("\">\n");
667: }
668:
669: public void unparsedEntityDecl(String name, String pubId,
670: String sysId, String notationName) throws SAXException {
671: if (D.ON && log.finerable())
672: log.finer("externalEntityDecl: " + name + " p:" + pubId
673: + " s:" + sysId + " n:" + notationName);
674:
675: if (!_inInternSubset)
676: return;
677:
678: _internSubset.append(" <!ENTITY ").append(name);
679:
680: if (pubId != null)
681: _internSubset.append(" PUBLIC \"").append(pubId).append(
682: "\" ");
683: if (sysId != null)
684: _internSubset.append(" SYSTEM \"").append(sysId).append(
685: "\" ");
686:
687: _internSubset.append(" NDATA ").append(notationName).append(
688: ">\n");
689: }
690:
691: //-- org.xml.sax.EntityResolver --//
692: public InputSource resolveEntity(String publicId, String systemId)
693: throws SAXException {
694: if (D.ON && log.finerable())
695: log.finer("resolveEntity public=" + publicId + " system="
696: + systemId);
697:
698: EntityResolver er = getEntityResolver();
699: if (er != null) {
700: try {
701: return er.resolveEntity(publicId, systemId);
702: } catch (IOException ex) {
703: //unfortunately, DefaultHandler doesn't throw IOException,
704: //so we have to wrap it
705: throw new SAXException(ex);
706: }
707: } else {
708: InputSource is = defaultResolveEntity(publicId, systemId);
709: if (D.ON && is == null && log.finerable())
710: log.finer("Unable to resolve public=" + publicId
711: + " system=" + systemId);
712: return is;
713: }
714: }
715:
716: /** The default entity resolver.
717: * It is used if {@link #setEntityResolver} is not called.
718: * This implementation searches the class path under /metainfo/xml.
719: *
720: * <p>For example, http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
721: * is asked.
722: * It searches from classpath
723: * for /metainfo/xml/java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
724: * and /metainfo/xml/portlet-app_1_0.xsd
725: */
726: protected InputSource defaultResolveEntity(String publicId,
727: String systemId) throws SAXException {
728: if (systemId == null)
729: return null;
730:
731: //Try the original form first if "file:/" found (including jar:file:/)
732: //We don't need to handle others, because http:// will be resolved
733: //by parser. And, if we do it here, we lost the chance to improve
734: //performance by loading it locally by below codes
735: if (systemId.indexOf("file:/") >= 0) {
736: try {
737: final InputSource is = new InputSource(
738: new URL(systemId).openStream());
739: is.setSystemId(systemId);
740: if (D.ON && log.finerable())
741: log.finer("Entity found " + systemId);
742: return is;
743: } catch (Exception ex) {
744: if (D.ON && log.finerable())
745: log.finer("Unable to open " + systemId);
746: }
747: }
748:
749: final String PREFIX = "/metainfo/xml";
750: final org.zkoss.util.resource.Locator loader = Locators
751: .getDefault();
752:
753: URL url = null;
754: int j = systemId.indexOf("://");
755: if (j > 0) {
756: final String resId = PREFIX + systemId.substring(j + 2);
757: url = loader.getResource(resId);
758: }
759: if (url == null) {
760: j = systemId.lastIndexOf('/');
761: final String resId = j >= 0 ? PREFIX
762: + systemId.substring(j) : PREFIX + '/' + systemId;
763: url = loader.getResource(resId);
764: }
765: if (url != null) {
766: if (D.ON && log.finerable())
767: log.finer("Entity resovled to " + url);
768: try {
769: final InputSource is = new InputSource(url.openStream());
770: is.setSystemId(url.toExternalForm());
771: return is;
772: } catch (IOException ex) {
773: throw new SAXException(ex); //not possible because Locator is used
774: }
775: }
776: return null;
777: }
778:
779: //-- org.xml.sax.ErrorHandler --//
780: public void warning(SAXParseException ex) throws SAXException {
781: ErrorHandler eh = getErrorHandler();
782: if (eh != null) {
783: eh.warning(ex);
784: } else {
785: log.warning(ex.getMessage() + SimpleLocator.toString(_loc));
786: }
787: }
788:
789: public void error(SAXParseException ex) throws SAXException {
790: ErrorHandler eh = getErrorHandler();
791: if (eh != null) {
792: eh.error(ex);
793: } else {
794: log.error(ex.getMessage() + SimpleLocator.toString(_loc));
795: throw ex;
796: }
797: }
798:
799: public void fatalError(SAXParseException ex) throws SAXException {
800: ErrorHandler eh = getErrorHandler();
801: if (eh != null) {
802: eh.fatalError(ex);
803: } else {
804: if (log.debugable())
805: log.debug(ex.getMessage()
806: + SimpleLocator.toString(_loc));
807: throw ex;
808: }
809: }
810: }
|