1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 /*
  21  * $Id: DOM2TO.java,v 1.5 2005/09/28 13:48:44 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.trax;
  25 
  26 import java.io.IOException;
  27 
  28 import org.w3c.dom.NamedNodeMap;
  29 import org.w3c.dom.Node;
  30 import org.w3c.dom.Document;
  31 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  32 import org.xml.sax.ContentHandler;
  33 import org.xml.sax.DTDHandler;
  34 import org.xml.sax.EntityResolver;
  35 import org.xml.sax.ErrorHandler;
  36 import org.xml.sax.InputSource;
  37 import org.xml.sax.ext.Locator2;
  38 import org.xml.sax.SAXException;
  39 import org.xml.sax.SAXNotRecognizedException;
  40 import org.xml.sax.SAXNotSupportedException;
  41 import org.xml.sax.XMLReader;
  42 import com.sun.org.apache.xml.internal.serializer.NamespaceMappings;
  43 
  44 /**
  45  * @author Santiago Pericas-Geertsen
  46  * @author Sunitha Reddy
  47  */
  48 public class DOM2TO implements XMLReader, Locator2 {
  49 
  50     private final static String EMPTYSTRING = "";
  51     private static final String XMLNS_PREFIX = "xmlns";
  52 
  53     /**
  54      * A reference to the DOM to be traversed.
  55      */
  56     private Node _dom;
  57 
  58     /**
  59      * A reference to the output handler receiving the events.
  60      */
  61     private SerializationHandler _handler;
  62 
  63 
  64     private String xmlVersion = null;
  65 
  66     private String xmlEncoding = null;
  67 
  68 
  69     public DOM2TO(Node root, SerializationHandler handler) {
  70         _dom = root;
  71         _handler = handler;
  72     }
  73 
  74     public ContentHandler getContentHandler() {
  75         return null;
  76     }
  77 
  78     public void setContentHandler(ContentHandler handler) {
  79         // Empty
  80     }
  81 
  82     public void parse(InputSource unused) throws IOException, SAXException {
  83         parse(_dom);
  84     }
  85 
  86     public void parse() throws IOException, SAXException {
  87 
  88         if (_dom != null) {
  89             boolean isIncomplete =
  90                 (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
  91 
  92             if (isIncomplete) {
  93                 _handler.startDocument();
  94                 parse(_dom);
  95                 _handler.endDocument();
  96             }
  97             else {
  98                 parse(_dom);
  99             }
 100         }
 101     }
 102 
 103     /**
 104      * Traverse the DOM and generate TO events for a handler. Notice that
 105      * we need to handle implicit namespace declarations too.
 106      */
 107     private void parse(Node node)
 108         throws IOException, SAXException
 109     {
 110         if (node == null) return;
 111 
 112         switch (node.getNodeType()) {
 113         case Node.ATTRIBUTE_NODE:         // handled by ELEMENT_NODE
 114         case Node.DOCUMENT_TYPE_NODE :
 115         case Node.ENTITY_NODE :
 116         case Node.ENTITY_REFERENCE_NODE:
 117         case Node.NOTATION_NODE :
 118             // These node types are ignored!!!
 119             break;
 120         case Node.CDATA_SECTION_NODE:
 121             _handler.startCDATA();
 122             _handler.characters(node.getNodeValue());
 123             _handler.endCDATA();
 124             break;
 125 
 126         case Node.COMMENT_NODE:           // should be handled!!!
 127             _handler.comment(node.getNodeValue());
 128             break;
 129 
 130         case Node.DOCUMENT_NODE:
 131              setDocumentInfo((Document)node);
 132              _handler.setDocumentLocator(this);
 133              _handler.startDocument();
 134             Node next = node.getFirstChild();
 135             while (next != null) {
 136                 parse(next);
 137                 next = next.getNextSibling();
 138             }
 139             _handler.endDocument();
 140             break;
 141 
 142         case Node.DOCUMENT_FRAGMENT_NODE:
 143             next = node.getFirstChild();
 144             while (next != null) {
 145                 parse(next);
 146                 next = next.getNextSibling();
 147             }
 148             break;
 149 
 150         case Node.ELEMENT_NODE:
 151             // Generate SAX event to start element
 152             final String qname = node.getNodeName();
 153             _handler.startElement(null, null, qname);
 154 
 155             int colon;
 156             String prefix;
 157             final NamedNodeMap map = node.getAttributes();
 158             final int length = map.getLength();
 159 
 160             // Process all namespace attributes first
 161             for (int i = 0; i < length; i++) {
 162                 final Node attr = map.item(i);
 163                 final String qnameAttr = attr.getNodeName();
 164 
 165                 // Is this a namespace declaration?
 166                 if (qnameAttr.startsWith(XMLNS_PREFIX)) {
 167                     final String uriAttr = attr.getNodeValue();
 168                     colon = qnameAttr.lastIndexOf(':');
 169                     prefix = (colon > 0) ? qnameAttr.substring(colon + 1)
 170                                          : EMPTYSTRING;
 171                     _handler.namespaceAfterStartElement(prefix, uriAttr);
 172                 }
 173             }
 174 
 175             // Process all non-namespace attributes next
 176             NamespaceMappings nm = new NamespaceMappings();
 177             for (int i = 0; i < length; i++) {
 178                 final Node attr = map.item(i);
 179                 final String qnameAttr = attr.getNodeName();
 180 
 181                 // Is this a regular attribute?
 182                 if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
 183                     final String uriAttr = attr.getNamespaceURI();
 184                     // Uri may be implicitly declared
 185                     if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) {
 186                         colon = qnameAttr.lastIndexOf(':');
 187 
 188                         // Fix for bug 26319
 189                         // For attributes not given an prefix explictly
 190                         // but having a namespace uri we need
 191                         // to explicitly generate the prefix
 192                         String newPrefix = nm.lookupPrefix(uriAttr);
 193                         if (newPrefix == null)
 194                             newPrefix = nm.generateNextPrefix();
 195                         prefix = (colon > 0) ? qnameAttr.substring(0, colon)
 196                             : newPrefix;
 197                         _handler.namespaceAfterStartElement(prefix, uriAttr);
 198                         _handler.addAttribute((prefix + ":" + qnameAttr),
 199                             attr.getNodeValue());
 200                     } else {
 201                          _handler.addAttribute(qnameAttr, attr.getNodeValue());
 202                     }
 203                 }
 204             }
 205 
 206             // Now element namespace and children
 207             final String uri = node.getNamespaceURI();
 208             final String localName = node.getLocalName();
 209 
 210             // Uri may be implicitly declared
 211             if (uri != null) {
 212                 colon = qname.lastIndexOf(':');
 213                 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
 214                 _handler.namespaceAfterStartElement(prefix, uri);
 215             }else {
 216                   // Fix for bug 26319
 217                   // If an element foo is created using
 218                   // createElementNS(null,locName)
 219                   // then the  element should be serialized
 220                   // <foo xmlns=" "/>
 221                   if (uri == null  && localName != null) {
 222                      prefix = EMPTYSTRING;
 223                      _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
 224                  }
 225             }
 226 
 227             // Traverse all child nodes of the element (if any)
 228             next = node.getFirstChild();
 229             while (next != null) {
 230                 parse(next);
 231                 next = next.getNextSibling();
 232             }
 233 
 234             // Generate SAX event to close element
 235             _handler.endElement(qname);
 236             break;
 237 
 238         case Node.PROCESSING_INSTRUCTION_NODE:
 239             _handler.processingInstruction(node.getNodeName(),
 240                                            node.getNodeValue());
 241             break;
 242 
 243         case Node.TEXT_NODE:
 244             _handler.characters(node.getNodeValue());
 245             break;
 246         }
 247     }
 248 
 249     /**
 250      * This class is only used internally so this method should never
 251      * be called.
 252      */
 253     public DTDHandler getDTDHandler() {
 254         return null;
 255     }
 256 
 257     /**
 258      * This class is only used internally so this method should never
 259      * be called.
 260      */
 261     public ErrorHandler getErrorHandler() {
 262         return null;
 263     }
 264 
 265     /**
 266      * This class is only used internally so this method should never
 267      * be called.
 268      */
 269     public boolean getFeature(String name) throws SAXNotRecognizedException,
 270         SAXNotSupportedException
 271     {
 272         return false;
 273     }
 274 
 275     /**
 276      * This class is only used internally so this method should never
 277      * be called.
 278      */
 279     public void setFeature(String name, boolean value) throws
 280         SAXNotRecognizedException, SAXNotSupportedException
 281     {
 282     }
 283 
 284     /**
 285      * This class is only used internally so this method should never
 286      * be called.
 287      */
 288     public void parse(String sysId) throws IOException, SAXException {
 289         throw new IOException("This method is not yet implemented.");
 290     }
 291 
 292     /**
 293      * This class is only used internally so this method should never
 294      * be called.
 295      */
 296     public void setDTDHandler(DTDHandler handler) throws NullPointerException {
 297     }
 298 
 299     /**
 300      * This class is only used internally so this method should never
 301      * be called.
 302      */
 303     public void setEntityResolver(EntityResolver resolver) throws
 304         NullPointerException
 305     {
 306     }
 307 
 308     /**
 309      * This class is only used internally so this method should never
 310      * be called.
 311      */
 312     public EntityResolver getEntityResolver() {
 313         return null;
 314     }
 315 
 316     /**
 317      * This class is only used internally so this method should never
 318      * be called.
 319      */
 320     public void setErrorHandler(ErrorHandler handler) throws
 321         NullPointerException
 322     {
 323     }
 324 
 325     /**
 326      * This class is only used internally so this method should never
 327      * be called.
 328      */
 329     public void setProperty(String name, Object value) throws
 330         SAXNotRecognizedException, SAXNotSupportedException {
 331     }
 332 
 333     /**
 334      * This class is only used internally so this method should never
 335      * be called.
 336      */
 337     public Object getProperty(String name) throws SAXNotRecognizedException,
 338         SAXNotSupportedException
 339     {
 340         return null;
 341     }
 342 
 343     /**
 344      * This class is only used internally so this method should never
 345      * be called.
 346      */
 347     public int getColumnNumber() {
 348         return 0;
 349     }
 350 
 351     /**
 352      * This class is only used internally so this method should never
 353      * be called.
 354      */
 355     public int getLineNumber() {
 356         return 0;
 357     }
 358 
 359     /**
 360      * This class is only used internally so this method should never
 361      * be called.
 362      */
 363     public String getPublicId() {
 364         return null;
 365     }
 366 
 367     /**
 368      * This class is only used internally so this method should never
 369      * be called.
 370      */
 371     public String getSystemId() {
 372         return null;
 373     }
 374 
 375 
 376     private void setDocumentInfo(Document document) {
 377         if (!document.getXmlStandalone())
 378             _handler.setStandalone(Boolean.toString(document.getXmlStandalone()));
 379         setXMLVersion(document.getXmlVersion());
 380         setEncoding(document.getXmlEncoding());
 381     }
 382 
 383     public String getXMLVersion() {
 384         return xmlVersion;
 385     }
 386 
 387     private void setXMLVersion(String version) {
 388         if (version != null) {
 389             xmlVersion = version;
 390             _handler.setVersion(xmlVersion);
 391         }
 392     }
 393 
 394     public String getEncoding() {
 395         return xmlEncoding;
 396     }
 397 
 398     private void setEncoding(String encoding) {
 399         if (encoding != null) {
 400             xmlEncoding = encoding;
 401             _handler.setEncoding(encoding);
 402         }
 403     }
 404 
 405     // Debugging
 406     private String getNodeTypeFromCode(short code) {
 407         String retval = null;
 408         switch (code) {
 409         case Node.ATTRIBUTE_NODE :
 410             retval = "ATTRIBUTE_NODE"; break;
 411         case Node.CDATA_SECTION_NODE :
 412             retval = "CDATA_SECTION_NODE"; break;
 413         case Node.COMMENT_NODE :
 414             retval = "COMMENT_NODE"; break;
 415         case Node.DOCUMENT_FRAGMENT_NODE :
 416             retval = "DOCUMENT_FRAGMENT_NODE"; break;
 417         case Node.DOCUMENT_NODE :
 418             retval = "DOCUMENT_NODE"; break;
 419         case Node.DOCUMENT_TYPE_NODE :
 420             retval = "DOCUMENT_TYPE_NODE"; break;
 421         case Node.ELEMENT_NODE :
 422             retval = "ELEMENT_NODE"; break;
 423         case Node.ENTITY_NODE :
 424             retval = "ENTITY_NODE"; break;
 425         case Node.ENTITY_REFERENCE_NODE :
 426             retval = "ENTITY_REFERENCE_NODE"; break;
 427         case Node.NOTATION_NODE :
 428             retval = "NOTATION_NODE"; break;
 429         case Node.PROCESSING_INSTRUCTION_NODE :
 430             retval = "PROCESSING_INSTRUCTION_NODE"; break;
 431         case Node.TEXT_NODE:
 432             retval = "TEXT_NODE"; break;
 433         }
 434         return retval;
 435     }
 436 }