code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the  "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id$
   */
  
  package org.apache.xalan.xsltc.trax;
  
  import java.io.IOException;
  
  import org.w3c.dom.NamedNodeMap;
  import org.w3c.dom.Node;
  import org.apache.xml.serializer.SerializationHandler;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.DTDHandler;
  import org.xml.sax.EntityResolver;
  import org.xml.sax.ErrorHandler;
  import org.xml.sax.InputSource;
  import org.xml.sax.Locator;
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXNotRecognizedException;
  import org.xml.sax.SAXNotSupportedException;
  import org.xml.sax.XMLReader;
  import org.apache.xml.serializer.NamespaceMappings;
  
  /**
   * @author Santiago Pericas-Geertsen
   */
  public class DOM2TO implements XMLReader, Locator {
  
      private final static String EMPTYSTRING = "";
      private static final String XMLNS_PREFIX = "xmlns";
  
      /**
       * A reference to the DOM to be traversed.
       */
      private Node _dom;
  
      /**
       * A reference to the output handler receiving the events.
       */
      private SerializationHandler _handler;
  
      public DOM2TO(Node root, SerializationHandler handler) {
  	_dom = root;
  	_handler = handler;
      }
  
      public ContentHandler getContentHandler() { 
  	return null;
      }
  
      public void setContentHandler(ContentHandler handler) {
  	// Empty
      }
  
      public void parse(InputSource unused) throws IOException, SAXException {
          parse(_dom);
      }
  
      public void parse() throws IOException, SAXException {
  	if (_dom != null) {
  	    boolean isIncomplete = 
  		(_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
  
  	    if (isIncomplete) {
  		_handler.startDocument();
  		parse(_dom);
  		_handler.endDocument();
  	    }
  	    else {
  		parse(_dom);
  	    }
  	}
      }
  
      /**
       * Traverse the DOM and generate TO events for a handler. Notice that 
       * we need to handle implicit namespace declarations too.
       */
      private void parse(Node node) 
  	throws IOException, SAXException 
      {
   	if (node == null) return;
  
          switch (node.getNodeType()) {
  	case Node.ATTRIBUTE_NODE:         // handled by ELEMENT_NODE
  	case Node.DOCUMENT_TYPE_NODE :
  	case Node.ENTITY_NODE :
  	case Node.ENTITY_REFERENCE_NODE:
  	case Node.NOTATION_NODE :
  	    // These node types are ignored!!!
  	    break;
  	case Node.CDATA_SECTION_NODE:
  	    _handler.startCDATA();
  	    _handler.characters(node.getNodeValue());
  	    _handler.endCDATA();
  	    break;
  
  	case Node.COMMENT_NODE:           // should be handled!!!
  	    _handler.comment(node.getNodeValue());
  	    break;
  
  	case Node.DOCUMENT_NODE:
  	    _handler.startDocument();
  	    Node next = node.getFirstChild();
  	    while (next != null) {
  		parse(next);
  		next = next.getNextSibling();
  	    }
  	    _handler.endDocument();
  	    break;
  
  	case Node.DOCUMENT_FRAGMENT_NODE:
  	    next = node.getFirstChild();
  	    while (next != null) {
  		parse(next);
  		next = next.getNextSibling();
  	    }
  	    break;
  
  	case Node.ELEMENT_NODE:
  	    // Generate SAX event to start element
  	    final String qname = node.getNodeName();
  	    _handler.startElement(null, null, qname);
  
              int colon;
  	    String prefix;
  	    final NamedNodeMap map = node.getAttributes();
  	    final int length = map.getLength();
  
  	    // Process all namespace attributes first
  	    for (int i = 0; i < length; i++) {
  		final Node attr = map.item(i);
  		final String qnameAttr = attr.getNodeName();
  
                  // Is this a namespace declaration?
  		if (qnameAttr.startsWith(XMLNS_PREFIX)) {
  		    final String uriAttr = attr.getNodeValue();
  		    colon = qnameAttr.lastIndexOf(':');
  		    prefix = (colon > 0) ? qnameAttr.substring(colon + 1) 
  			                 : EMPTYSTRING;
  		    _handler.namespaceAfterStartElement(prefix, uriAttr);
  		}
  	    }
              
  	    // Process all non-namespace attributes next
              NamespaceMappings nm = new NamespaceMappings();
  	    for (int i = 0; i < length; i++) {
  		final Node attr = map.item(i);
  		final String qnameAttr = attr.getNodeName();
  
                  // Is this a regular attribute?
  		if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
  		    final String uriAttr = attr.getNamespaceURI();
  		    // Uri may be implicitly declared
  		    if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) {	
  			colon = qnameAttr.lastIndexOf(':');
                             
                          // Fix for bug 26319
                          // For attributes not given an prefix explictly
                          // but having a namespace uri we need
                          // to explicitly generate the prefix
                          String newPrefix = nm.lookupPrefix(uriAttr);
                          if (newPrefix == null) 
                              newPrefix = nm.generateNextPrefix();
  			prefix = (colon > 0) ? qnameAttr.substring(0, colon) 
  			    : newPrefix;
  			_handler.namespaceAfterStartElement(prefix, uriAttr);
  		        _handler.addAttribute((prefix + ":" + qnameAttr),
                              attr.getNodeValue());
  		    } else {
                           _handler.addAttribute(qnameAttr, attr.getNodeValue());
                      }
                  }
  	    }
  
  	    // Now element namespace and children
  	    final String uri = node.getNamespaceURI();
              final String localName = node.getLocalName();
  
  	    // Uri may be implicitly declared
  	    if (uri != null) {	
  		colon = qname.lastIndexOf(':');
  		prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
  		_handler.namespaceAfterStartElement(prefix, uri);
  	    }else {
                    // Fix for bug 26319
                    // If an element foo is created using
                    // createElementNS(null,locName)
                    // then the  element should be serialized
                    // <foo xmlns=" "/> 
                    if (uri == null  && localName != null) {
   		     prefix = EMPTYSTRING;
  		     _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
                   }
              }
  
  	    // Traverse all child nodes of the element (if any)
  	    next = node.getFirstChild();
  	    while (next != null) {
  		parse(next);
  		next = next.getNextSibling();
  	    }
  
  	    // Generate SAX event to close element
  	    _handler.endElement(qname);
  	    break;
  
  	case Node.PROCESSING_INSTRUCTION_NODE:
  	    _handler.processingInstruction(node.getNodeName(),
  					   node.getNodeValue());
  	    break;
  
  	case Node.TEXT_NODE:
  	    _handler.characters(node.getNodeValue());
  	    break;
  	}
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public DTDHandler getDTDHandler() { 
  	return null;
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public ErrorHandler getErrorHandler() {
  	return null;
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public boolean getFeature(String name) throws SAXNotRecognizedException,
  	SAXNotSupportedException
      {
  	return false;
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void setFeature(String name, boolean value) throws 
  	SAXNotRecognizedException, SAXNotSupportedException 
      {
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void parse(String sysId) throws IOException, SAXException {
  	throw new IOException("This method is not yet implemented.");
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void setDTDHandler(DTDHandler handler) throws NullPointerException {
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void setEntityResolver(EntityResolver resolver) throws 
  	NullPointerException 
      {
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public EntityResolver getEntityResolver() {
  	return null;
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void setErrorHandler(ErrorHandler handler) throws 
  	NullPointerException
      {
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public void setProperty(String name, Object value) throws
  	SAXNotRecognizedException, SAXNotSupportedException {
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public Object getProperty(String name) throws SAXNotRecognizedException,
  	SAXNotSupportedException
      {
  	return null;
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public int getColumnNumber() { 
  	return 0; 
      }
      
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public int getLineNumber() { 
  	return 0; 
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public String getPublicId() { 
  	return null; 
      }
  
      /**
       * This class is only used internally so this method should never 
       * be called.
       */
      public String getSystemId() { 
  	return null; 
      }
  
      // Debugging 
      private String getNodeTypeFromCode(short code) {
  	String retval = null;
  	switch (code) {
  	case Node.ATTRIBUTE_NODE : 
  	    retval = "ATTRIBUTE_NODE"; break; 
  	case Node.CDATA_SECTION_NODE :
  	    retval = "CDATA_SECTION_NODE"; break; 
  	case Node.COMMENT_NODE :
  	    retval = "COMMENT_NODE"; break; 
  	case Node.DOCUMENT_FRAGMENT_NODE :
  	    retval = "DOCUMENT_FRAGMENT_NODE"; break; 
  	case Node.DOCUMENT_NODE :
  	    retval = "DOCUMENT_NODE"; break; 
  	case Node.DOCUMENT_TYPE_NODE :
  	    retval = "DOCUMENT_TYPE_NODE"; break; 
  	case Node.ELEMENT_NODE :
  	    retval = "ELEMENT_NODE"; break; 
  	case Node.ENTITY_NODE :
  	    retval = "ENTITY_NODE"; break; 
  	case Node.ENTITY_REFERENCE_NODE :
  	    retval = "ENTITY_REFERENCE_NODE"; break; 
  	case Node.NOTATION_NODE :
  	    retval = "NOTATION_NODE"; break; 
  	case Node.PROCESSING_INSTRUCTION_NODE :
  	    retval = "PROCESSING_INSTRUCTION_NODE"; break; 
  	case Node.TEXT_NODE:
  	    retval = "TEXT_NODE"; break; 
          }
  	return retval;
      }
  }
  