001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.xml2;
030:
031: import com.caucho.vfs.Depend;
032: import com.caucho.vfs.Path;
033:
034: import org.w3c.dom.*;
035:
036: import javax.xml.namespace.QName;
037: import java.io.IOException;
038: import java.util.ArrayList;
039: import java.util.HashMap;
040: import java.util.Iterator;
041:
042: /**
043: * Implements the top-level document for the XML tree.
044: */
045: public class QDocument extends QDocumentFragment implements
046: CauchoDocument {
047: QDOMImplementation _implementation;
048: QDocumentType _dtd;
049: QElement _element; // top
050: HashMap<String, String> _attributes;
051: String _encoding = "UTF-8";
052: String _version;
053:
054: private String _systemId;
055:
056: private HashMap<String, String> _namespaces;
057:
058: private transient HashMap<NameKey, QName> _nameCache = new HashMap<NameKey, QName>();
059: private transient NameKey _nameKey = new NameKey();
060: private transient ArrayList<Path> _depends;
061: private transient ArrayList<Depend> _dependList;
062:
063: int _changeCount;
064:
065: // possibly different from the systemId if the DOCTYPE doesn't match
066: // the actual file location
067: String _rootFilename;
068: private boolean _standalone;
069:
070: public QDocument() {
071: _implementation = new QDOMImplementation();
072: _owner = this ;
073: }
074:
075: public QDocument(DocumentType docType) {
076: _owner = this ;
077: setDoctype(docType);
078: }
079:
080: public QDocument(QDOMImplementation impl) {
081: _implementation = impl;
082: _owner = this ;
083: }
084:
085: void setAttribute(String name, String value) {
086: if (name.equals("version"))
087: _version = value;
088: else if (name.equals("encoding"))
089: _encoding = value;
090: else {
091: if (_attributes == null)
092: _attributes = new HashMap<String, String>();
093: _attributes.put(name, value);
094: }
095: }
096:
097: public String getRootFilename() {
098: return _rootFilename;
099: }
100:
101: public void setRootFilename(String filename) {
102: _rootFilename = filename;
103: }
104:
105: public void setSystemId(String systemId) {
106: _systemId = systemId;
107: }
108:
109: public String getSystemId() {
110: return _systemId;
111: }
112:
113: /**
114: * Returns the base URI of the node.
115: */
116: public String getBaseURI() {
117: return getSystemId();
118: }
119:
120: public Document getOwnerDocument() {
121: return null;
122: }
123:
124: public DOMConfiguration getDomConfig() {
125: return null;
126: }
127:
128: public boolean isSupported(String feature, String version) {
129: return _owner.getImplementation().hasFeature(feature, version);
130: }
131:
132: /**
133: * The node name for the document is #document.
134: */
135: public String getNodeName() {
136: return "#document";
137: }
138:
139: public short getNodeType() {
140: return DOCUMENT_NODE;
141: }
142:
143: protected Node copyNode(QDocument newNode, boolean deep) {
144: newNode._dtd = _dtd;
145: newNode._element = _element;
146:
147: return newNode;
148: }
149:
150: /**
151: * Returns a clone of the document.
152: *
153: * @param deep if true, recursively copy the document.
154: */
155: public Node cloneNode(boolean deep) {
156: QDocument newDoc = new QDocument();
157:
158: newDoc._implementation = _implementation;
159: newDoc._dtd = _dtd;
160: if (_attributes != null)
161: newDoc._attributes = (HashMap) _attributes.clone();
162: newDoc._encoding = _encoding;
163: newDoc._version = _version;
164:
165: if (_namespaces != null)
166: newDoc._namespaces = (HashMap) _namespaces.clone();
167:
168: if (deep) {
169: for (Node node = getFirstChild(); node != null; node = node
170: .getNextSibling()) {
171: newDoc.appendChild(newDoc.importNode(node, true));
172: }
173: }
174:
175: return newDoc;
176: }
177:
178: Node importNode(QDocument doc, boolean deep) {
179: return null;
180: }
181:
182: /**
183: * Imports a copy of a node into the current document.
184: *
185: * @param node the node to import/copy
186: * @param deep if true, recursively copy the children.
187: *
188: * @return the new imported node.
189: */
190: public Node importNode(Node node, boolean deep) {
191: if (node == null)
192: return null;
193:
194: QName name;
195:
196: switch (node.getNodeType()) {
197: case ELEMENT_NODE:
198: return importElement((Element) node, deep);
199:
200: case ATTRIBUTE_NODE:
201: Attr attr = (Attr) node;
202: name = createName(attr.getNamespaceURI(), attr
203: .getNodeName());
204: QAttr newAttr = new QAttr(name, attr.getNodeValue());
205: newAttr._owner = this ;
206: return newAttr;
207:
208: case TEXT_NODE:
209: QText newText = new QText(node.getNodeValue());
210: newText._owner = this ;
211: return newText;
212:
213: case CDATA_SECTION_NODE:
214: QCdata newCData = new QCdata(node.getNodeValue());
215: newCData._owner = this ;
216: return newCData;
217:
218: case ENTITY_REFERENCE_NODE:
219: QEntityReference newER = new QEntityReference(node
220: .getNodeName());
221: newER._owner = this ;
222: return newER;
223:
224: case ENTITY_NODE:
225: Entity oldEntity = (Entity) node;
226: QEntity newEntity = new QEntity(oldEntity.getNodeName(),
227: oldEntity.getNodeValue(), oldEntity.getPublicId(),
228: oldEntity.getSystemId());
229: newEntity._owner = this ;
230: return newEntity;
231:
232: case PROCESSING_INSTRUCTION_NODE:
233: QProcessingInstruction newPI;
234: newPI = new QProcessingInstruction(node.getNodeName(), node
235: .getNodeValue());
236:
237: newPI._owner = this ;
238: return newPI;
239:
240: case COMMENT_NODE:
241: QComment newComment = new QComment(node.getNodeValue());
242: newComment._owner = this ;
243: return newComment;
244:
245: case DOCUMENT_FRAGMENT_NODE:
246: return importFragment((DocumentFragment) node, deep);
247:
248: default:
249: throw new UnsupportedOperationException(String
250: .valueOf(node));
251: }
252: }
253:
254: /**
255: * Imports an element.
256: */
257: private Element importElement(Element elt, boolean deep) {
258: QElement newElt = new QElement(createName(
259: elt.getNamespaceURI(), elt.getNodeName()));
260: QElement oldElt = null;
261:
262: if (elt instanceof QElement)
263: oldElt = (QElement) elt;
264:
265: newElt._owner = this ;
266:
267: if (oldElt != null) {
268: newElt._filename = oldElt._filename;
269: newElt._line = oldElt._line;
270: }
271:
272: NamedNodeMap attrs = elt.getAttributes();
273:
274: int len = attrs.getLength();
275: for (int i = 0; i < len; i++) {
276: Attr attr = (Attr) attrs.item(i);
277:
278: newElt.setAttributeNode((Attr) importNode(attr, deep));
279: }
280:
281: if (!deep)
282: return newElt;
283:
284: for (Node node = elt.getFirstChild(); node != null; node = node
285: .getNextSibling()) {
286: newElt.appendChild(importNode(node, true));
287: }
288:
289: return newElt;
290: }
291:
292: /**
293: * Imports an element.
294: */
295: private DocumentFragment importFragment(DocumentFragment elt,
296: boolean deep) {
297: QDocumentFragment newFrag = new QDocumentFragment();
298:
299: newFrag._owner = this ;
300:
301: if (!deep)
302: return newFrag;
303:
304: for (Node node = elt.getFirstChild(); node != null; node = node
305: .getNextSibling()) {
306: newFrag.appendChild(importNode(node, true));
307: }
308:
309: return newFrag;
310: }
311:
312: public DocumentType getDoctype() {
313: return _dtd;
314: }
315:
316: public void setDoctype(DocumentType dtd) {
317: QDocumentType qdtd = (QDocumentType) dtd;
318:
319: _dtd = qdtd;
320: if (qdtd != null)
321: qdtd._owner = this ;
322: }
323:
324: public String getEncoding() {
325: if (_encoding == null)
326: return null;
327: else
328: return _encoding;
329: }
330:
331: public DOMImplementation getImplementation() {
332: return _implementation;
333: }
334:
335: public Element getDocumentElement() {
336: return _element;
337: }
338:
339: public void setDocumentElement(Element elt) {
340: _element = (QElement) elt;
341: }
342:
343: /**
344: * Creates a new element
345: */
346: public Element createElement(String tagName) throws DOMException {
347: if (!isNameValid(tagName))
348: throw new QDOMException(DOMException.INVALID_CHARACTER_ERR,
349: "illegal tag `" + tagName + "'");
350:
351: QElement elt = new QElement(createName(null, tagName));
352: elt._owner = this ;
353:
354: return elt;
355: }
356:
357: /**
358: * Creates a new namespace-aware element
359: */
360: public Element createElementNS(String namespaceURI, String name)
361: throws DOMException {
362: QName qname = createName(namespaceURI, name);
363:
364: validateName(qname);
365: addNamespace(qname);
366:
367: QElement elt = new QElement(qname);
368: elt._owner = this ;
369:
370: return elt;
371: }
372:
373: public void validateName(QName qname) throws DOMException {
374: String prefix = qname.getPrefix();
375: String namespaceURI = qname.getNamespaceURI();
376:
377: if (qname.getPrefix() == "") {
378: } else if (prefix == "xml"
379: && namespaceURI != "http://www.w3.org/XML/1998/namespace")
380: throw new DOMException(
381: DOMException.NAMESPACE_ERR,
382: L
383: .l("`xml' prefix expects namespace uri 'http://www.w3.org/XML/1998/namespace'"));
384: else if (prefix != "" && prefix != null && namespaceURI == null)
385: throw new DOMException(DOMException.NAMESPACE_ERR, L.l(
386: "`{0}' prefix expects a namespace uri", prefix));
387:
388: }
389:
390: /**
391: * Creates a new namespace-aware element
392: */
393: public Element createElement(String prefix, String local, String url)
394: throws DOMException {
395: QName name = new QName(prefix, local, url);
396: addNamespace(name);
397:
398: QElement elt = new QElement(name);
399: elt._owner = this ;
400:
401: return elt;
402: }
403:
404: public Element createElementByName(QName name) throws DOMException {
405: QElement elt = new QElement(name);
406: elt._owner = this ;
407:
408: return elt;
409: }
410:
411: /**
412: * Creates a new document fragment.
413: */
414: public DocumentFragment createDocumentFragment() {
415: QDocumentFragment frag = new QDocumentFragment();
416: frag._owner = this ;
417:
418: return frag;
419: }
420:
421: /**
422: * Creates a new text node in this document.
423: */
424: public Text createTextNode(String data) {
425: if (data == null)
426: data = "";
427:
428: QText text = new QText(data);
429: text._owner = this ;
430:
431: return text;
432: }
433:
434: public Text createUnescapedTextNode(String data) {
435: if (data == null)
436: data = "";
437:
438: QText text = new QUnescapedText(data);
439: text._owner = this ;
440:
441: return text;
442: }
443:
444: public Comment createComment(String data) {
445: if (data == null)
446: data = "";
447:
448: QComment comment = new QComment(data);
449: comment._owner = this ;
450:
451: return comment;
452: }
453:
454: public CDATASection createCDATASection(String data) {
455: if (data == null)
456: data = "";
457:
458: QCdata cdata = new QCdata(data);
459: cdata._owner = this ;
460:
461: return cdata;
462: }
463:
464: public ProcessingInstruction createProcessingInstruction(
465: String target, String data) throws DOMException {
466: if (target == null || target.length() == 0)
467: throw new QDOMException(
468: DOMException.INVALID_CHARACTER_ERR,
469: L
470: .l("Empty processing instruction name. The processing instruction syntax is: <?name ... ?>"));
471:
472: if (!isNameValid(target))
473: throw new QDOMException(
474: DOMException.INVALID_CHARACTER_ERR,
475: L
476: .l(
477: "`{0}' is an invalid processing instruction name. The processing instruction syntax is: <?name ... ?>",
478: target));
479:
480: if (data == null)
481: data = "";
482:
483: QProcessingInstruction pi = new QProcessingInstruction(target,
484: data);
485: pi._owner = this ;
486:
487: return pi;
488: }
489:
490: public Attr createAttribute(String name, String value)
491: throws DOMException {
492: if (!isNameValid(name))
493: throw new QDOMException(DOMException.INVALID_CHARACTER_ERR,
494: "illegal attribute `" + name + "'");
495:
496: if (value == null)
497: value = "";
498:
499: QAttr attr = new QAttr(new QName(null, name, null), value);
500: attr._owner = this ;
501:
502: return attr;
503: }
504:
505: public Attr createAttribute(String name) throws DOMException {
506: return createAttribute(name, null);
507: }
508:
509: /**
510: * Creates a new namespace-aware attribute
511: */
512: public Attr createAttribute(String prefix, String local, String url)
513: throws DOMException {
514: QName name = new QName(prefix, local, url);
515: if (url != null && !url.equals(""))
516: addNamespace(prefix, url);
517:
518: QAttr attr = new QAttr(name, null);
519: attr._owner = this ;
520:
521: return attr;
522: }
523:
524: /**
525: * Creates a new namespace-aware attribute
526: */
527: public Attr createAttributeNS(String namespaceURI,
528: String qualifiedName) throws DOMException {
529: QName qname = createName(namespaceURI, qualifiedName);
530:
531: validateName(qname);
532: addNamespace(qname);
533:
534: /* xml/0213
535: else if (name.getNamespace() == "")
536: throw new DOMException(DOMException.NAMESPACE_ERR,
537: L.l("`{0}' prefix expects a namespace uri",
538: name.getPrefix()));
539: */
540:
541: QAttr attr = new QAttr(qname, null);
542: attr._owner = this ;
543:
544: return attr;
545: }
546:
547: public QName createName(String uri, String name) {
548: _nameKey.init(name, uri);
549: QName qName = _nameCache.get(_nameKey);
550:
551: if (qName != null)
552: return qName;
553:
554: if (uri == null) {
555: qName = new QName(null, name, null);
556: } else {
557: int p = name.indexOf(':');
558: String prefix;
559: String local;
560: if (p < 0) {
561: prefix = null;
562: local = name;
563: } else {
564: prefix = name.substring(0, p);
565: local = name.substring(p + 1);
566: }
567:
568: qName = new QName(prefix, local, uri);
569: }
570:
571: _nameCache.put(new NameKey(name, uri), qName);
572:
573: return qName;
574: }
575:
576: /**
577: * Creates a new namespace-aware attribute
578: */
579: public Attr createAttribute(QName name, String value)
580: throws DOMException {
581: String url = name.getNamespaceURI();
582:
583: if (url != null && url != "") {
584: addNamespace(name.getPrefix(), url);
585: }
586:
587: QAttr attr = new QAttr(name, value);
588: attr._owner = this ;
589:
590: return attr;
591: }
592:
593: public EntityReference createEntityReference(String name)
594: throws DOMException {
595: if (!isNameValid(name))
596: throw new QDOMException(DOMException.INVALID_CHARACTER_ERR,
597: "illegal entityReference `" + name + "'");
598:
599: QEntityReference er = new QEntityReference(name);
600: er._owner = this ;
601:
602: return er;
603: }
604:
605: /**
606: * Returns a list of elements, filtered by the tag name.
607: */
608: public NodeList getElementsByTagName(String name) {
609: if (_element == null)
610: return new QDeepNodeList(null, null, null);
611: else
612: return new QDeepNodeList(_element, _element,
613: new QElement.TagPredicate(name));
614: }
615:
616: public NodeList getElementsByTagNameNS(String uri, String name) {
617: if (_element == null)
618: return new QDeepNodeList(null, null, null);
619: else
620: return new QDeepNodeList(_element, _element,
621: new QElement.NSTagPredicate(uri, name));
622: }
623:
624: public Element getElementById(String name) {
625: Node node = _element;
626:
627: for (; node != null; node = XmlUtil.getNext(node)) {
628: if (node instanceof Element) {
629: Element elt = (Element) node;
630:
631: String id = elt.getAttribute("id");
632:
633: if (name.equals(id))
634: return elt;
635: }
636: }
637:
638: return null;
639: }
640:
641: static public Document create() {
642: QDocument doc = new QDocument();
643: doc._masterDoc = doc;
644:
645: return doc;
646: }
647:
648: void setAttributes(HashMap<String, String> attributes) {
649: _attributes = attributes;
650: }
651:
652: public Node appendChild(Node newChild) throws DOMException {
653: if (newChild instanceof Element) {
654: _element = (QElement) newChild;
655:
656: // xml/0201
657: if (false && _namespaces != null) {
658: Iterator<String> iter = _namespaces.keySet().iterator();
659:
660: while (iter.hasNext()) {
661: String prefix = iter.next();
662: String ns = _namespaces.get(prefix);
663:
664: String xmlns;
665:
666: if (prefix.equals(""))
667: xmlns = "xmlns";
668: else
669: xmlns = "xmlns:" + prefix;
670:
671: if (_element.getAttribute(xmlns).equals("")) {
672: QName qName = new QName(xmlns, XmlParser.XMLNS);
673: _element.setAttributeNode(createAttribute(
674: qName, ns));
675: }
676: }
677: }
678: }
679:
680: return super .appendChild(newChild);
681: }
682:
683: public Node removeChild(Node oldChild) throws DOMException {
684: Node value = super .removeChild(oldChild);
685: if (oldChild == _element)
686: _element = null;
687: return value;
688: }
689:
690: // non-DOM
691:
692: public void addNamespace(QName qname) {
693: addNamespace(qname.getPrefix(), qname.getNamespaceURI());
694: }
695:
696: /**
697: * Add a namespace declaration to a document. If the declaration
698: * prefix already has a namespace, the old one wins.
699: */
700: public void addNamespace(String prefix, String url) {
701: if (url == null || url.length() == 0
702: || XmlParser.XMLNS.equals(url)
703: || XmlParser.XML.equals(url)) {
704: return;
705: }
706:
707: if (prefix == null)
708: prefix = "";
709:
710: if (_namespaces == null)
711: _namespaces = new HashMap<String, String>();
712:
713: String old = _namespaces.get(prefix);
714: if (old == null)
715: _namespaces.put(prefix, url.intern());
716: }
717:
718: public HashMap<String, String> getNamespaces() {
719: return _namespaces;
720: }
721:
722: /**
723: * Returns the namespace url for a given prefix.
724: */
725: public String getNamespace(String prefix) {
726: if (_namespaces == null)
727: return null;
728: else
729: return _namespaces.get(prefix);
730: }
731:
732: /**
733: * Returns an iterator of top-level namespace prefixes.
734: */
735: public Iterator<String> getNamespaceKeys() {
736: if (_namespaces == null)
737: return null;
738:
739: return _namespaces.keySet().iterator();
740: }
741:
742: public Object getProperty(String name) {
743: if (name.equals(DEPENDS))
744: return _depends;
745: else
746: return null;
747: }
748:
749: public ArrayList<Path> getDependList() {
750: return _depends;
751: }
752:
753: public ArrayList<Depend> getDependencyList() {
754: return _dependList;
755: }
756:
757: public void setProperty(String name, Object value) {
758: if (name.equals(DEPENDS))
759: _depends = (ArrayList) value;
760: }
761:
762: // DOM LEVEL 3
763: public String getActualEncoding() {
764: throw new UnsupportedOperationException();
765: }
766:
767: public void setActualEncoding(String actualEncoding) {
768: throw new UnsupportedOperationException();
769: }
770:
771: /*
772: public String getEncoding()
773: {
774: throw new UnsupportedOperationException();
775: }
776: */
777:
778: public void setEncoding(String encoding) {
779: throw new UnsupportedOperationException();
780: }
781:
782: public boolean getStandalone() {
783: return _standalone;
784: }
785:
786: public void setStandalone(boolean standalone) {
787: _standalone = true;
788: }
789:
790: public String getXmlVersion() {
791: return _version;
792: }
793:
794: public void setXmlVersion(String version) throws DOMException {
795: _version = version;
796: }
797:
798: public void setXmlStandalone(boolean value) throws DOMException {
799: }
800:
801: public TypeInfo getSchemaTypeInfo() {
802: return null;
803: }
804:
805: public String getXmlEncoding() {
806: return null;
807: }
808:
809: public String getInputEncoding() {
810: return null;
811: }
812:
813: public boolean getXmlStandalone() throws DOMException {
814: return false;
815: }
816:
817: public boolean getStrictErrorChecking() {
818: throw new UnsupportedOperationException();
819: }
820:
821: public void setStrictErrorChecking(boolean strictErrorChecking) {
822: throw new UnsupportedOperationException();
823: }
824:
825: public DOMErrorHandler getErrorHandler() {
826: throw new UnsupportedOperationException();
827: }
828:
829: public void setErrorHandler(DOMErrorHandler errorHandler) {
830: throw new UnsupportedOperationException();
831: }
832:
833: public String getDocumentURI() {
834: throw new UnsupportedOperationException();
835: }
836:
837: public void setDocumentURI(String documentURI) {
838: throw new UnsupportedOperationException();
839: }
840:
841: public Node adoptNode(Node source) throws DOMException {
842: throw new UnsupportedOperationException();
843: }
844:
845: public void normalizeDocument() {
846: throw new UnsupportedOperationException();
847: }
848:
849: public boolean canSetNormalizationFeature(String name, boolean state) {
850: throw new UnsupportedOperationException();
851: }
852:
853: public void setNormalizationFeature(String name, boolean state)
854: throws DOMException {
855: throw new UnsupportedOperationException();
856: }
857:
858: public boolean getNormalizationFeature(String name)
859: throws DOMException {
860: throw new UnsupportedOperationException();
861: }
862:
863: public Node renameNode(Node n, String namespaceURI, String name)
864: throws DOMException {
865: throw new UnsupportedOperationException();
866: }
867:
868: // CAUCHO
869:
870: public void addDepend(Path path) {
871: if (path == null)
872: return;
873:
874: if (_depends == null)
875: _depends = new ArrayList<Path>();
876:
877: if (!_depends.contains(path)) {
878: _depends.add(path);
879:
880: if (_dependList == null)
881: _dependList = new ArrayList<Depend>();
882:
883: _dependList.add(new Depend(path));
884: }
885: }
886:
887: public boolean isModified() {
888: if (_dependList == null)
889: return false;
890:
891: for (int i = 0; i < _dependList.size(); i++) {
892: Depend depend = _dependList.get(i);
893:
894: if (depend.isModified())
895: return true;
896: }
897:
898: return false;
899: }
900:
901: void print(XmlPrinter os) throws IOException {
902: os.startDocument(this );
903:
904: if (_namespaces != null) {
905: Iterator<String> iter = _namespaces.keySet().iterator();
906: while (iter.hasNext()) {
907: String prefix = iter.next();
908: String url = _namespaces.get(prefix);
909:
910: if (prefix.equals(""))
911: os.attribute(null, prefix, "xmlns", url);
912: else
913: os.attribute(null, prefix, "xmlns:" + prefix, url);
914: }
915: }
916:
917: if (getFirstChild() == null)
918: os.printHeader(null);
919:
920: for (Node node = getFirstChild(); node != null; node = node
921: .getNextSibling()) {
922: ((QAbstractNode) node).print(os);
923: if (os.isPretty())
924: os.println();
925: }
926:
927: os.endDocument();
928: }
929:
930: public String toString() {
931: String topElt = _element == null ? "XXX:top" : _element
932: .getNodeName();
933:
934: if (_dtd == null)
935: return "Document[" + topElt + "]";
936:
937: if (_dtd.getPublicId() != null && _dtd.getSystemId() != null)
938: return ("Document[" + topElt + " PUBLIC '"
939: + _dtd.getPublicId() + "' '" + _dtd.getSystemId() + "']");
940: else if (_dtd._publicId != null)
941: return "Document[" + topElt + " PUBLIC '"
942: + _dtd.getPublicId() + "']";
943: else if (_dtd.getSystemId() != null)
944: return "Document[" + topElt + " SYSTEM '"
945: + _dtd.getSystemId() + "']";
946: else
947: return "Document[" + topElt + "]";
948: }
949:
950: static class NameKey {
951: String _qName;
952: String _url;
953:
954: NameKey() {
955: }
956:
957: NameKey(String qName, String url) {
958: init(qName, url);
959: }
960:
961: void init(String qName, String url) {
962: if (qName == null)
963: throw new NullPointerException();
964:
965: if (url == null)
966: url = "";
967:
968: _qName = qName;
969: _url = url;
970: }
971:
972: public int hashCode() {
973: return 65521 * _url.hashCode() + _qName.hashCode();
974: }
975:
976: public boolean equals(Object b) {
977: if (!(b instanceof NameKey))
978: return false;
979:
980: NameKey key = (NameKey) b;
981:
982: return _qName.equals(key._qName) && _url.equals(key._url);
983: }
984: }
985:
986: private Object writeReplace() {
987: return new SerializedXml(this);
988: }
989: }
|