KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > converters > DOMConverter


1 /* Copyright 2002-2004 Elliotte Rusty Harold
2    
3    This library is free software; you can redistribute it and/or modify
4    it under the terms of version 2.1 of the GNU Lesser General Public
5    License as published by the Free Software Foundation.
6    
7    This library is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10    GNU Lesser General Public License for more details.
11    
12    You should have received a copy of the GNU Lesser General Public
13    License along with this library; if not, write to the
14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15    Boston, MA 02111-1307 USA
16    
17    You can contact Elliotte Rusty Harold by sending e-mail to
18    elharo@metalab.unc.edu. Please include the word "XOM" in the
19    subject line. The XOM home page is located at http://www.xom.nu/
20 */

21
22 package nu.xom.converters;
23
24 import nu.xom.Attribute;
25 import nu.xom.Comment;
26 import nu.xom.DocType;
27 import nu.xom.Document;
28 import nu.xom.Element;
29 import nu.xom.Node;
30 import nu.xom.Nodes;
31 import nu.xom.ParentNode;
32 import nu.xom.ProcessingInstruction;
33 import nu.xom.Text;
34 import nu.xom.XMLException;
35
36 import org.w3c.dom.Attr JavaDoc;
37 import org.w3c.dom.DOMImplementation JavaDoc;
38 import org.w3c.dom.DocumentFragment JavaDoc;
39 import org.w3c.dom.DocumentType JavaDoc;
40 import org.w3c.dom.NamedNodeMap JavaDoc;
41 import org.w3c.dom.NodeList JavaDoc;
42 // Many DOM interfaces such as Element and Document
43
// have name conflicts with XOM classes.
44
// Thus they cannot be imported, and this class
45
// must use their fully package qualified names.
46

47
48 /**
49  * <p>
50  * Converts XOM <code>Document</code> objects to and from DOM
51  * <code>Document</code> objects. This class can also
52  * convert many DOM node objects into the corresponding
53  * XOM node objects. However, the reverse is not possible because
54  * DOM objects cannot live outside their containing
55  * <code>Document</code>.
56  * </p>
57  *
58  * @author Elliotte Rusty Harold
59  * @version 1.0
60  *
61  */

62 public class DOMConverter {
63
64     
65     // prevent instantiation
66
private DOMConverter() {}
67     
68     
69     /**
70      * <p>
71      * DOM violates the namespaces 1.0 specification by mapping
72      * the <code>xmlns</code> prefix to the namespace URI
73      * <code>http://www.w3.org/2000/xmlns/</code>.
74      * </p>
75      */

76     private final static String JavaDoc XMLNS_NAMESPACE
77       = "http://www.w3.org/2000/xmlns/";
78
79     
80     /**
81      * <p>
82      * Translates a DOM <code>org.w3c.dom.Document</code> object
83      * into an equivalent <code>nu.xom.Document</code> object.
84      * The original DOM document is not changed.
85      * Some DOM <code>Document</code> objects cannot
86      * be serialized as namespace well-formed XML, and
87      * thus cannot be converted to XOM.
88      * </p>
89      *
90      * @param domDocument the DOM document to translate
91      * @return a XOM document
92      *
93      * @throws XMLException if the DOM document is not a well-formed
94      * XML document
95      */

96     public static Document convert(org.w3c.dom.Document JavaDoc domDocument) {
97         
98         org.w3c.dom.Element JavaDoc domRoot = domDocument.getDocumentElement();
99         Element xomRoot = convert(domRoot);
100         Document xomDocument = new Document(xomRoot);
101         
102         org.w3c.dom.Node JavaDoc current = domDocument.getFirstChild();
103         
104         // prolog
105
for (int position = 0;
106              current.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE;
107              position++, current = current.getNextSibling()) {
108             xomDocument.insertChild(convert(current), position);
109         }
110         // root element
111
current = current.getNextSibling();
112         
113         // epilog
114
while (current != null) {
115             xomDocument.appendChild(convert(current));
116             current = current.getNextSibling();
117         }
118                        
119         return xomDocument;
120         
121     }
122
123     
124     /**
125      * <p>
126      * Translates a DOM <code>org.w3c.dom.DocumentFragment</code>
127      * object into an equivalent <code>nu.xom.Nodes</code> object.
128      * The original DOM document fragment is not changed.
129      * Some DOM <code>DocumentFragment</code> objects cannot
130      * be serialized as namespace well-balanced XML, and
131      * thus cannot be converted to XOM.
132      * </p>
133      *
134      * @param fragment the DOM document fragment to translate
135      *
136      * @return a <code>Nodes</code> containing the converted
137      * fragment members
138      *
139      * @throws XMLException if the DOM object is not a well-balanced
140      * XML fragment
141      */

142     public static Nodes convert(DocumentFragment JavaDoc fragment) {
143         
144         Nodes result = new Nodes();
145         NodeList JavaDoc children = fragment.getChildNodes();
146         for (int i = 0; i < children.getLength(); i++) {
147             result.append(convert(children.item(i)));
148         }
149      
150         return result;
151         
152     }
153
154     
155     private static Node convert(org.w3c.dom.Node JavaDoc node) {
156         
157         int type = node.getNodeType();
158         switch (type) {
159             case org.w3c.dom.Node.ELEMENT_NODE:
160                 return convert((org.w3c.dom.Element JavaDoc) node);
161             case org.w3c.dom.Node.COMMENT_NODE:
162                 return convert((org.w3c.dom.Comment JavaDoc) node);
163             case org.w3c.dom.Node.DOCUMENT_TYPE_NODE:
164                 return convert((org.w3c.dom.DocumentType JavaDoc) node);
165             case org.w3c.dom.Node.TEXT_NODE:
166                 return convert((org.w3c.dom.Text JavaDoc) node);
167             case org.w3c.dom.Node.CDATA_SECTION_NODE:
168                 return convert((org.w3c.dom.Text JavaDoc) node);
169             case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
170                 return convert((org.w3c.dom.ProcessingInstruction JavaDoc) node);
171             default:
172                 throw new XMLException(
173                   "Unexpected DOM node type: " + type);
174         }
175         
176     }
177
178     
179     /**
180      * <p>
181      * Translates a DOM <code>org.w3c.dom.Comment</code> object
182      * into an equivalent <code>nu.xom.Comment</code> object.
183      * The original DOM object is not changed.
184      * Some DOM <code>Comment</code> objects cannot
185      * be serialized as well-formed XML, and
186      * thus cannot be converted to XOM.
187      * </p>
188      *
189      * @param comment the DOM comment to translate
190      * @return a XOM comment
191      *
192      * @throws XMLException if the DOM comment is not a well-formed
193      * XML comment
194      */

195     public static Comment convert(org.w3c.dom.Comment JavaDoc comment) {
196         return new Comment(comment.getNodeValue());
197     }
198
199     /**
200      * <p>
201      * Translates a DOM <code>org.w3c.dom.Text</code> object
202      * into an equivalent <code>nu.xom.Text</code>.
203      * This method will also convert <code>org.w3c.dom.CDATA</code>
204      * objects. The original DOM object is not changed.
205      * Some DOM <code>Text</code> objects cannot
206      * be serialized as well-formed XML, and
207      * thus cannot be converted to XOM.
208      * </p>
209      *
210      * @param text the DOM text to translate
211      * @return a XOM text
212      *
213      * @throws XMLException if the DOM text is not a well-formed
214      * XML text
215      */

216     public static Text convert(org.w3c.dom.Text JavaDoc text) {
217         return new Text(text.getNodeValue());
218     }
219
220     
221     /**
222      * <p>
223      * Translates a DOM <code>org.w3c.dom.Attr</code> object
224      * into an equivalent <code>nu.xom.Attribute</code> object.
225      * The original DOM object is not changed.
226      * Some DOM <code>Attr</code> objects cannot
227      * be serialized as well-formed XML, and
228      * thus cannot be converted to XOM. Furthermore, DOM uses
229      * <code>Attr</code> objects to represent namespace declarations.
230      * XOM does not. Converting an <code>Attr</code> object that
231      * represents an <code>xmlns</code> or
232      * <code>xmlns:<i>prefix</i></code> attribute will cause an
233      * exception.
234      * </p>
235      *
236      * @param attribute the DOM <code>Attr</code> to translate
237      * @return the equivalent XOM <code>Attribute</code>
238      *
239      * @throws XMLException if the DOM <code>Attr</code>
240      * is a namespace declaration or is not a well-formed
241      * XML attribute
242      */

243     public static Attribute convert(Attr JavaDoc attribute) {
244         
245         String JavaDoc name = attribute.getName();
246         String JavaDoc uri = attribute.getNamespaceURI();
247         if (uri == null) uri = "";
248         return new Attribute(name, uri, attribute.getNodeValue());
249         
250     }
251
252
253     /**
254      * <p>
255      * Translates a DOM <code>org.w3c.dom.ProcessingInstruction</code>
256      * object into an equivalent
257      * <code>nu.xom.ProcessingInstruction</code> object.
258      * The original DOM object is not changed.
259      * Some DOM <code>ProcessingInstruction</code> objects cannot
260      * be serialized as well-formed XML, and
261      * thus cannot be converted to XOM.
262      * </p>
263      *
264      * @param pi the DOM <code>ProcessingInstruction</code> to
265      * convert
266      * @return a XOM <code>ProcessingInstruction</code>
267      *
268      * @throws XMLException if the DOM <code>ProcessingInstruction</code>
269      * is not a well-formed XML processing instruction
270      */

271     public static ProcessingInstruction convert(
272         org.w3c.dom.ProcessingInstruction JavaDoc pi) {
273         return new ProcessingInstruction(
274           pi.getTarget(), pi.getNodeValue());
275     }
276
277     
278     /**
279      * <p>
280      * Translates a DOM <code>org.w3c.dom.DocumentType</code>
281      * object into an equivalent <code>nu.xom.DocType</code> object.
282      * The original DOM object is not changed. The internal DTD subset
283      * is not converted, but the root element name, system identifier,
284      * and public identifier are. Some DOM <code>DocumentType</code>
285      * objects cannot be serialized as well-formed XML, and
286      * thus cannot be converted to XOM.
287      * </p>
288      *
289      * @param doctype the DOM <code>DocumentType</code> to convert
290      * @return the equivalent XOM <code>DocType</code>
291      *
292      * @throws XMLException if the DOM <code>DocumentType</code>
293      * is not a well-formed XML document type declaration
294      */

295     public static DocType convert(org.w3c.dom.DocumentType JavaDoc doctype) {
296
297         DocType result =
298             new DocType(
299                 doctype.getName(),
300                 doctype.getPublicId(),
301                 doctype.getSystemId());
302
303         return result;
304
305     }
306
307     
308     /**
309      * <p>
310      * Translates a DOM <code>org.w3c.dom.Element</code>
311      * object into an equivalent <code>nu.xom.Element</code> object.
312      * The original DOM object is not changed. Some DOM
313      * <code>Element</code> objects cannot be serialized as
314      * namespace well-formed XML, and thus cannot be converted to XOM.
315      * </p>
316      *
317      * @param element the DOM <code>Element</code> to convert
318      * @return the equivalent XOM <code>Element</code>
319      *
320      * @throws XMLException if the DOM <code>Element</code>
321      * is not a well-formed XML element
322      */

323     public static Element convert(org.w3c.dom.Element JavaDoc element) {
324         
325         org.w3c.dom.Node JavaDoc current = element;
326         Element result = makeElement(element);
327         ParentNode parent = result;
328         boolean backtracking = false;
329         while (true) {
330             if (current.hasChildNodes() && !backtracking) {
331                 current = current.getFirstChild();
332                 backtracking = false;
333             }
334             else if (current == element) {
335                 break;
336             }
337             else if (current.getNextSibling() != null) {
338                 current = current.getNextSibling();
339                 backtracking = false;
340             }
341             else {
342                 current = current.getParentNode();
343                 backtracking = true;
344                 parent = parent.getParent();
345                 continue;
346             }
347             
348             int type = current.getNodeType();
349             if (type == org.w3c.dom.Node.ELEMENT_NODE) {
350                 Element child = makeElement((org.w3c.dom.Element JavaDoc) current);
351                 parent.appendChild(child);
352                 if (current.hasChildNodes()) parent = child;
353             }
354             else {
355                 Node child = convert(current);
356                 parent.appendChild(child);
357             }
358             
359         }
360         
361         return result;
362         
363     }
364  
365     
366     private static Element makeElement(org.w3c.dom.Element JavaDoc element) {
367         
368         String JavaDoc namespaceURI = element.getNamespaceURI();
369         String JavaDoc tagName = element.getTagName();
370         Element result = new Element(tagName, namespaceURI);
371         
372         // fill element's attributes and additional namespace declarations
373
NamedNodeMap JavaDoc attributes = element.getAttributes();
374         for (int i = 0; i < attributes.getLength(); i++) {
375             org.w3c.dom.Attr JavaDoc attribute = (org.w3c.dom.Attr JavaDoc) attributes.item(i);
376             String JavaDoc name = attribute.getName();
377             String JavaDoc uri = attribute.getNamespaceURI();
378             String JavaDoc value = attribute.getValue();
379             if (uri == null) uri = "";
380             if (uri.equals(XMLNS_NAMESPACE)) {
381                 if (name.equals("xmlns")) continue;
382                 String JavaDoc prefix = name.substring(name.indexOf(':') + 1);
383                 String JavaDoc currentURI = result.getNamespaceURI(prefix);
384                 if (!value.equals(currentURI)) {
385                     result.addNamespaceDeclaration(prefix, value);
386                 }
387             }
388             else {
389                 result.addAttribute(new Attribute(name, uri, value));
390             }
391         }
392         return result;
393         
394     }
395
396
397     /**
398      * <p>
399      * Translates a XOM <code>nu.xom.Document</code> object
400      * into an equivalent <code>org.w3c.dom.Document</code>
401      * object. The original XOM document is not changed.
402      * Since DOM2 internal subsets are read-only,
403      * the internal DTD subset is not converted.
404      * All other aspects of the document should be
405      * translated without a problem.
406      * </p>
407      *
408      * @param document the XOM document to translate
409      * @param impl the specific DOM implementation into which this
410      * document will be converted
411      *
412      * @return a DOM document
413      */

414     public static org.w3c.dom.Document JavaDoc convert(Document document,
415       DOMImplementation JavaDoc impl) {
416
417         Element root = document.getRootElement();
418         String JavaDoc rootName = root.getQualifiedName();
419         String JavaDoc rootNamespace = root.getNamespaceURI();
420         DocType doctype = document.getDocType();
421         DocumentType JavaDoc domDOCTYPE = null;
422         if (doctype != null) {
423             domDOCTYPE = impl.createDocumentType(rootName,
424             doctype.getPublicID(), doctype.getSystemID());
425         }
426         
427         org.w3c.dom.Document JavaDoc domDoc
428          = impl.createDocument(rootNamespace, rootName, domDOCTYPE);
429         org.w3c.dom.Element JavaDoc domRoot = domDoc.getDocumentElement();
430         
431         boolean beforeRoot = true;
432         for (int i = 0; i < document.getChildCount(); i++) {
433             Node original = document.getChild(i);
434             // Need to test positioning of doctype
435
if (original instanceof DocType) continue;
436             else if (original instanceof Element) {
437                 convert((Element) original, domDoc);
438                 beforeRoot = false;
439             }
440             else {
441                 org.w3c.dom.Node JavaDoc domNode = convert(original, domDoc);
442                 if (beforeRoot) domDoc.insertBefore(domNode, domRoot);
443                 else domDoc.appendChild(domNode);
444             }
445         }
446         
447         return domDoc;
448         
449     }
450
451     
452     private static org.w3c.dom.Node JavaDoc convert(
453       Node node, org.w3c.dom.Document JavaDoc document) {
454         
455         if (node instanceof Text) {
456             return convert((Text) node, document);
457         }
458         else if (node instanceof Comment) {
459             return convert((Comment) node, document);
460         }
461         else if (node instanceof ProcessingInstruction) {
462             return convert((ProcessingInstruction) node, document);
463         }
464         // The non-recursive algorithm converts elements directly.
465
// It does not pass through this method.
466
else {
467             throw new XMLException(
468               "Unexpected node type: " + node.getClass().getName()
469             );
470         }
471          
472     }
473
474     
475     private static org.w3c.dom.Comment JavaDoc convert(
476       Comment comment, org.w3c.dom.Document JavaDoc document) {
477         return document.createComment(comment.getValue());
478     }
479
480     
481     private static org.w3c.dom.Text JavaDoc convert(
482       Text text, org.w3c.dom.Document JavaDoc document) {
483         return document.createTextNode(text.getValue());
484     }
485
486     
487     private static org.w3c.dom.ProcessingInstruction JavaDoc convert(
488       ProcessingInstruction pi, org.w3c.dom.Document JavaDoc document) {
489         return document.createProcessingInstruction(
490           pi.getTarget(), pi.getValue());
491     }
492
493     
494     private static org.w3c.dom.Element JavaDoc convert(
495       Element xomElement, org.w3c.dom.Document JavaDoc document) {
496         
497         org.w3c.dom.Element JavaDoc domResult = makeElement(xomElement, document);
498         org.w3c.dom.Node JavaDoc domParent = domResult;
499         Node xomCurrent = xomElement;
500         int index = 0;
501         int[] indexes = new int[10];
502         int top = 0;
503         indexes[0] = 0;
504         boolean end = false;
505         while (true) {
506             
507             if (!end && xomCurrent.getChildCount() > 0) {
508                xomCurrent = xomCurrent.getChild(0);
509                index = 0;
510                top++;
511                indexes = grow(indexes, top);
512                indexes[top] = 0;
513             }
514             else {
515                 boolean wasEnd = end;
516                 end = false;
517                 ParentNode xomParent = xomCurrent.getParent();
518                 org.w3c.dom.Node JavaDoc grandparent = domParent.getParentNode();
519                 if (grandparent.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
520                   && xomCurrent instanceof Element) {
521                     domParent = grandparent;
522                 }
523                 if (xomParent.getChildCount() - 1 == index) {
524                     xomCurrent = xomParent;
525                     top--;
526                     if (xomCurrent == xomElement) break;
527                     ParentNode tp = xomCurrent.getParent();
528                     if (tp == null) break;
529                     index = indexes[top];
530                     end = true;
531                     continue;
532                 }
533                 else {
534                     index++;
535                     indexes[top] = index;
536                     xomCurrent = xomParent.getChild(index);
537                 }
538             }
539             
540             if (xomCurrent instanceof Element) {
541                 Element currentElement = (Element) xomCurrent;
542                 org.w3c.dom.Element JavaDoc child = makeElement(currentElement, document);
543                 domParent.appendChild(child);
544                 domParent = child;
545             }
546             else {
547                 org.w3c.dom.Node JavaDoc child = convert(xomCurrent, document);
548                 domParent.appendChild(child);
549             }
550             
551         } // end while
552

553         return domResult;
554         
555     }
556         
557     
558     private static int[] grow(int[] indexes, int top) {
559         
560         if (top < indexes.length) return indexes;
561         int[] result = new int[indexes.length*2];
562         System.arraycopy(indexes, 0, result, 0, indexes.length);
563         return result;
564         
565     }
566
567
568     private static org.w3c.dom.Element JavaDoc makeElement(
569       Element element, org.w3c.dom.Document JavaDoc document) {
570         
571         org.w3c.dom.Element JavaDoc result;
572         String JavaDoc namespace = element.getNamespaceURI();
573          
574         if (element.getParent() instanceof Document) {
575             result = document.getDocumentElement();
576         }
577         else if (namespace.equals("")) {
578             result = document.createElement(
579               element.getQualifiedName());
580         }
581         else {
582             result = document.createElementNS(
583               namespace, element.getQualifiedName());
584         }
585                 
586         int attributeCount = element.getAttributeCount();
587         for (int i = 0; i < attributeCount; i++) {
588             Attribute attribute = element.getAttribute(i);
589             String JavaDoc attns = attribute.getNamespaceURI();
590             Attr JavaDoc attr;
591             if (attns.equals("")) {
592                 attr = document.createAttribute(attribute.getLocalName());
593                 result.setAttributeNode(attr);
594             }
595             else {
596                 attr = document.createAttributeNS(
597                   attns, attribute.getQualifiedName()
598                 );
599                 result.setAttributeNodeNS(attr);
600             }
601             attr.setValue(attribute.getValue());
602         }
603         
604         int namespaceCount = element.getNamespaceDeclarationCount();
605         for (int i = 0; i < namespaceCount; i++) {
606             String JavaDoc additionalPrefix = element.getNamespacePrefix(i);
607             String JavaDoc uri = element.getNamespaceURI(additionalPrefix);
608
609             ParentNode parentNode = element.getParent();
610             if (parentNode instanceof Element) {
611                Element parentElement = (Element) parentNode;
612                if (uri.equals(
613                  parentElement.getNamespaceURI(additionalPrefix))) {
614                     continue;
615                }
616             }
617             else if (uri.equals("")) { //parent is Document or null
618
continue; // no need to say xmlns=""
619
}
620
621             if ("".equals(additionalPrefix)) {
622                 Attr JavaDoc attr = document.createAttributeNS(
623                   XMLNS_NAMESPACE, "xmlns"
624                 );
625                 result.setAttributeNodeNS(attr);
626                 attr.setValue(uri);
627             }
628             else {
629                 Attr JavaDoc attr = document.createAttributeNS(
630                   XMLNS_NAMESPACE,
631                   "xmlns:" + additionalPrefix
632                 );
633                 result.setAttributeNodeNS(attr);
634                 attr.setValue(uri);
635             }
636         }
637         
638         return result;
639         
640     }
641
642     
643 }
644
Popular Tags