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