1:
37:
38: package ;
39:
40: import ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62: import ;
63:
64:
106: public class DomConsumer implements EventConsumer
107: {
108: private Class domImpl;
109:
110: private boolean hidingCDATA = true;
111: private boolean hidingComments = true;
112: private boolean hidingWhitespace = true;
113: private boolean hidingReferences = true;
114:
115: private Handler handler;
116: private ErrorHandler errHandler;
117:
118: private EventConsumer next;
119:
120:
121:
122:
123:
124:
125:
135: public DomConsumer (Class impl)
136: throws SAXException
137: {
138: domImpl = impl;
139: handler = new Handler (this);
140: }
141:
142:
149: protected void setHandler (Handler h)
150: {
151: handler = h;
152: }
153:
154:
155: private Document emptyDocument ()
156: throws SAXException
157: {
158: try {
159: return (Document) domImpl.newInstance ();
160: } catch (IllegalAccessException e) {
161: throw new SAXException ("can't access constructor: "
162: + e.getMessage ());
163: } catch (InstantiationException e) {
164: throw new SAXException ("can't instantiate Document: "
165: + e.getMessage ());
166: }
167: }
168:
169:
170:
188: public DomConsumer (Class impl, EventConsumer n)
189: throws SAXException
190: {
191: this (impl);
192: next = n;
193: }
194:
195:
196:
202: final public Document getDocument ()
203: {
204: return handler.clearDocument ();
205: }
206:
207: public void setErrorHandler (ErrorHandler handler)
208: {
209: errHandler = handler;
210: }
211:
212:
213:
222: final public boolean isHidingReferences ()
223: { return hidingReferences; }
224:
225:
232: final public void setHidingReferences (boolean flag)
233: { hidingReferences = flag; }
234:
235:
236:
242: public final boolean isHidingComments ()
243: { return hidingComments; }
244:
245:
250: public final void setHidingComments (boolean flag)
251: { hidingComments = flag; }
252:
253:
254:
261: public final boolean isHidingWhitespace ()
262: { return hidingWhitespace; }
263:
264:
269: public final void setHidingWhitespace (boolean flag)
270: { hidingWhitespace = flag; }
271:
272:
273:
279: final public boolean isHidingCDATA ()
280: { return hidingCDATA; }
281:
282:
289: final public void setHidingCDATA (boolean flag)
290: { hidingCDATA = flag; }
291:
292:
293:
294:
295: final public ContentHandler getContentHandler ()
296: { return handler; }
297:
298:
299: final public DTDHandler getDTDHandler ()
300: { return handler; }
301:
302:
306: final public Object getProperty (String id)
307: throws SAXNotRecognizedException
308: {
309: if ("http://xml.org/sax/properties/lexical-handler".equals (id))
310: return handler;
311: if ("http://xml.org/sax/properties/declaration-handler".equals (id))
312: return handler;
313: throw new SAXNotRecognizedException (id);
314: }
315:
316: EventConsumer getNext () { return next; }
317:
318: ErrorHandler getErrorHandler () { return errHandler; }
319:
320:
331: public static class Handler
332: implements ContentHandler, LexicalHandler,
333: DTDHandler, DeclHandler
334: {
335: protected DomConsumer consumer;
336:
337: private DOMImplementation impl;
338: private Document document;
339: private boolean isL2;
340:
341: private Locator locator;
342: private Node top;
343: private boolean inCDATA;
344: private boolean mergeCDATA;
345: private boolean inDTD;
346: private String currentEntity;
347:
348: private boolean recreatedAttrs;
349: private AttributesImpl attributes = new AttributesImpl ();
350:
351:
355: protected Handler (DomConsumer consumer)
356: throws SAXException
357: {
358: this.consumer = consumer;
359: document = consumer.emptyDocument ();
360: impl = document.getImplementation ();
361: isL2 = impl.hasFeature ("XML", "2.0");
362: }
363:
364: private void fatal (String message, Exception x)
365: throws SAXException
366: {
367: SAXParseException e;
368: ErrorHandler errHandler = consumer.getErrorHandler ();
369:
370: if (locator == null)
371: e = new SAXParseException (message, null, null, -1, -1, x);
372: else
373: e = new SAXParseException (message, locator, x);
374: if (errHandler != null)
375: errHandler.fatalError (e);
376: throw e;
377: }
378:
379:
383: Document clearDocument ()
384: {
385: Document retval = document;
386: document = null;
387: locator = null;
388: return retval;
389: }
390:
391:
394: protected Document getDocument ()
395: { return document; }
396:
397:
403: protected Node getTop ()
404: { return top; }
405:
406:
407:
408: public void setDocumentLocator (Locator locator)
409: {
410: this.locator = locator;
411: }
412:
413:
414: public void startDocument ()
415: throws SAXException
416: {
417: if (document == null)
418: try {
419: if (isL2) {
420:
421: document = impl.createDocument (null, "foo", null);
422: document.removeChild (document.getFirstChild ());
423: } else {
424: document = consumer.emptyDocument ();
425: }
426: } catch (Exception e) {
427: fatal ("DOM create document", e);
428: }
429: top = document;
430: }
431:
432:
433: public void endDocument ()
434: throws SAXException
435: {
436: try {
437: if (consumer.getNext () != null && document != null) {
438: DomParser parser = new DomParser (document);
439:
440: EventFilter.bind (parser, consumer.getNext ());
441: parser.parse ("ignored");
442: }
443: } finally {
444: top = null;
445: }
446: }
447:
448:
449: public void processingInstruction (String target, String data)
450: throws SAXException
451: {
452:
453:
454: if (currentEntity != null)
455: return;
456:
457: ProcessingInstruction pi;
458:
459: if (isL2
460:
461: && target.indexOf (':') != -1)
462: namespaceError (
463: "PI target name is namespace nonconformant: "
464: + target);
465: if (inDTD)
466: return;
467: pi = document.createProcessingInstruction (target, data);
468: top.appendChild (pi);
469: }
470:
471:
482: protected Text createText (
483: boolean isCDATA,
484: char ch [],
485: int start,
486: int length
487: ) {
488: String value = new String (ch, start, length);
489:
490: if (isCDATA)
491: return document.createCDATASection (value);
492: else
493: return document.createTextNode (value);
494: }
495:
496:
497: public void characters (char ch [], int start, int length)
498: throws SAXException
499: {
500:
501:
502:
503: if (currentEntity != null)
504: return;
505:
506: Node lastChild = top.getLastChild ();
507:
508:
509: if (lastChild instanceof Text) {
510: if (consumer.isHidingCDATA ()
511:
512: || (!inCDATA
513: && !(lastChild instanceof CDATASection))
514:
515:
516: || (inCDATA && mergeCDATA
517: && lastChild instanceof CDATASection)
518: ) {
519: CharacterData last = (CharacterData) lastChild;
520: String value = new String (ch, start, length);
521:
522: last.appendData (value);
523: return;
524: }
525: }
526: if (inCDATA && !consumer.isHidingCDATA ()) {
527: top.appendChild (createText (true, ch, start, length));
528: mergeCDATA = true;
529: } else
530: top.appendChild (createText (false, ch, start, length));
531: }
532:
533:
534: public void skippedEntity (String name)
535: throws SAXException
536: {
537:
538:
539:
540:
541: fatal ("skipped entity: " + name, null);
542: }
543:
544:
545: public void startPrefixMapping (String prefix, String uri)
546: throws SAXException
547: {
548:
549:
550: if ("".equals (prefix))
551: attributes.addAttribute ("", "", "xmlns",
552: "CDATA", uri);
553: else
554: attributes.addAttribute ("", "", "xmlns:" + prefix,
555: "CDATA", uri);
556: recreatedAttrs = true;
557: }
558:
559:
560: public void endPrefixMapping (String prefix)
561: throws SAXException
562: { }
563:
564:
565: public void startElement (
566: String uri,
567: String localName,
568: String qName,
569: Attributes atts
570: ) throws SAXException
571: {
572:
573:
574: if (currentEntity != null)
575: return;
576:
577:
578:
579:
580: if (qName.length () == 0)
581: qName = localName;
582:
583:
584: Element element;
585: int length = atts.getLength ();
586:
587: if (!isL2) {
588: element = document.createElement (qName);
589:
590:
591: length = atts.getLength ();
592: for (int i = 0; i < length; i++)
593: element.setAttribute (atts.getQName (i),
594: atts.getValue (i));
595:
596: if (recreatedAttrs) {
597: recreatedAttrs = false;
598: length = attributes.getLength ();
599: for (int i = 0; i < length; i++)
600: element.setAttribute (attributes.getQName (i),
601: attributes.getValue (i));
602: attributes.clear ();
603: }
604:
605: top.appendChild (element);
606: top = element;
607: return;
608: }
609:
610:
611:
612:
613:
614: String namespace;
615:
616: if (localName.length () != 0)
617: namespace = (uri.length () == 0) ? null : uri;
618: else
619: namespace = getNamespace (getPrefix (qName), atts);
620:
621: if (namespace == null)
622: element = document.createElement (qName);
623: else
624: element = document.createElementNS (namespace, qName);
625:
626: populateAttributes (element, atts);
627: if (recreatedAttrs) {
628: recreatedAttrs = false;
629:
630: populateAttributes (element, attributes);
631: attributes.clear ();
632: }
633:
634: top.appendChild (element);
635: top = element;
636: }
637:
638: final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
639:
640: private void populateAttributes (Element element, Attributes attrs)
641: throws SAXParseException
642: {
643: int length = attrs.getLength ();
644:
645: for (int i = 0; i < length; i++) {
646: String type = attrs.getType (i);
647: String value = attrs.getValue (i);
648: String name = attrs.getQName (i);
649: String local = attrs.getLocalName (i);
650: String uri = attrs.getURI (i);
651:
652:
653: if (name.length () == 0)
654: name = local;
655:
656:
657:
658:
659: if (!("CDATA".equals (type)
660: || "NMTOKEN".equals (type)
661: || "NMTOKENS".equals (type))) {
662: if (value.indexOf (':') != -1) {
663: namespaceError (
664: "namespace nonconformant attribute value: "
665: + "<" + element.getNodeName ()
666: + " " + name + "='" + value + "' ...>");
667: }
668: }
669:
670:
671:
672: String prefix = getPrefix (name);
673: String namespace;
674:
675: if ("xmlns".equals (prefix)) {
676: if ("".equals (value))
677: namespaceError ("illegal null namespace decl, " + name);
678: namespace = xmlnsURI;
679: } else if ("xmlns".equals (name))
680: namespace = xmlnsURI;
681:
682: else if (prefix == null)
683: namespace = null;
684: else if (!"".equals(uri) && uri.length () != 0)
685: namespace = uri;
686: else
687: namespace = getNamespace (prefix, attrs);
688:
689: if (namespace == null)
690: element.setAttribute (name, value);
691: else
692: element.setAttributeNS (namespace, name, value);
693: }
694: }
695:
696: private String getPrefix (String name)
697: {
698: int temp;
699:
700: if ((temp = name.indexOf (':')) > 0)
701: return name.substring (0, temp);
702: return null;
703: }
704:
705:
706: private String getNamespace (String prefix, Attributes attrs)
707: throws SAXParseException
708: {
709: String namespace;
710: String decl;
711:
712:
713: if (prefix == null) {
714: decl = "xmlns";
715: namespace = attrs.getValue (decl);
716: if ("".equals (namespace))
717: return null;
718: else if (namespace != null)
719: return namespace;
720:
721:
722:
723:
724:
725: } else if ("xmlns".equals (prefix))
726: return null;
727:
728:
729: else if ("xml".equals (prefix))
730: return "http://www.w3.org/XML/1998/namespace";
731:
732:
733: else {
734: decl = "xmlns:" + prefix;
735: namespace = attrs.getValue (decl);
736: }
737:
738:
739: if (namespace != null)
740: return namespace;
741:
742:
743:
744: for (Node n = top;
745: n != null && n.getNodeType () != Node.DOCUMENT_NODE;
746: n = n.getParentNode ()) {
747: if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE)
748: continue;
749: Element e = (Element) n;
750: Attr attr = e.getAttributeNode (decl);
751: if (attr != null)
752: return attr.getNodeValue ();
753: }
754:
755: if ("xmlns".equals (decl))
756: return null;
757:
758: namespaceError ("Undeclared namespace prefix: " + prefix);
759: return null;
760: }
761:
762:
763: public void endElement (String uri, String localName, String qName)
764: throws SAXException
765: {
766:
767:
768: if (currentEntity != null)
769: return;
770:
771: top = top.getParentNode ();
772: }
773:
774:
775: public void ignorableWhitespace (char ch [], int start, int length)
776: throws SAXException
777: {
778: if (consumer.isHidingWhitespace ())
779: return;
780: characters (ch, start, length);
781: }
782:
783:
784: public void startCDATA ()
785: throws SAXException
786: {
787: inCDATA = true;
788:
789: mergeCDATA = false;
790: }
791:
792:
793: public void endCDATA ()
794: throws SAXException
795: {
796: inCDATA = false;
797: }
798:
799:
800:
801:
802:
803:
804:
805:
806:
807:
808:
809:
810: public void startDTD (String name, String publicId, String SystemId)
811: throws SAXException
812: {
813:
814: inDTD = true;
815: }
816:
817:
818: public void endDTD ()
819: throws SAXException
820: {
821: inDTD = false;
822: }
823:
824:
825: public void comment (char ch [], int start, int length)
826: throws SAXException
827: {
828: Node comment;
829:
830:
831:
832: if (consumer.isHidingComments ()
833: || inDTD
834: || currentEntity != null)
835: return;
836: comment = document.createComment (new String (ch, start, length));
837: top.appendChild (comment);
838: }
839:
840:
845: public boolean canPopulateEntityRefs ()
846: { return false; }
847:
848:
849: public void startEntity (String name)
850: throws SAXException
851: {
852:
853:
854: if (currentEntity != null)
855: return;
856:
857:
858: if (consumer.isHidingReferences ())
859: return;
860:
861:
862: if (name.charAt (0) == '%' || "[dtd]".equals (name))
863: return;
864:
865:
866:
867: EntityReference ref = document.createEntityReference (name);
868: top.appendChild (ref);
869: top = ref;
870:
871:
872: if (!canPopulateEntityRefs ())
873: currentEntity = name;
874: }
875:
876:
877: public void endEntity (String name)
878: throws SAXException
879: {
880: if (name.charAt (0) == '%' || "[dtd]".equals (name))
881: return;
882: if (name.equals (currentEntity))
883: currentEntity = null;
884: if (!consumer.isHidingReferences ())
885: top = top.getParentNode ();
886: }
887:
888:
889:
890: public void notationDecl (
891: String name,
892: String publicId, String SystemId
893: ) throws SAXException
894: {
895:
898: }
899:
900:
901: public void unparsedEntityDecl (
902: String name,
903: String publicId, String SystemId,
904: String notationName
905: ) throws SAXException
906: {
907:
910: }
911:
912:
913: public void elementDecl (String name, String model)
914: throws SAXException
915: {
916:
917: }
918:
919:
920: public void attributeDecl (
921: String eName,
922: String aName,
923: String type,
924: String mode,
925: String value
926: ) throws SAXException
927: {
928:
929: }
930:
931:
932: public void internalEntityDecl (String name, String value)
933: throws SAXException
934: {
935:
938: }
939:
940:
941: public void externalEntityDecl (
942: String name,
943: String publicId,
944: String SystemId
945: ) throws SAXException
946: {
947:
950: }
951:
952:
953:
954:
955:
956:
957:
958: private void namespaceError (String description)
959: throws SAXParseException
960: {
961: SAXParseException err;
962:
963: err = new SAXParseException (description, locator);
964: throw err;
965: }
966: }
967: }