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