package org.dbxml.xml;

/*
 * dbXML License, Version 1.0
 *
 *
 * Copyright (c) 1999-2001 The dbXML Group, L.L.C.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by
 *        The dbXML Group (http://www.dbxml.com/)."
 *    Alternately, this acknowledgment may appear in the software
 *    itself, if and wherever such third-party acknowledgments normally
 *    appear.
 *
 * 4. The names "dbXML" and "The dbXML Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    info@dbxml.com.
 *
 * 5. Products derived from this software may not be called "dbXML",
 *    nor may "dbXML" appear in their name, without prior written
 *    permission of The dbXML Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE DBXML GROUP OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id: TextWriter.java,v 1.18 2001/08/10 00:13:10 tom Exp $
 */

import org.w3c.dom.*;
import java.io.*;
import java.util.*;

/**
 * TextWriter takes a Document, DocumentFragment, or Element and streams it
 * as text to an output source (or a String)
 */

public final class TextWriter {
   private Node node = null;

   public TextWriter(Node node) throws DOMException {
      this.node = node;
   }

   public TextWriter(Document document) {
      node = document;
   }

   public TextWriter(Element element) {
      node = element;
   }

   public TextWriter(DocumentFragment fragment) {
      node = fragment;
   }

   private void writeNode(Writer writer, Node node) throws IOException {
      short type = node.getNodeType();
      switch ( type ) {

         case Node.DOCUMENT_NODE: {
            writer.write("<?xml version=\"1.0\"?>\n");
            writeChildren(writer, node);
            break;
         }

         case Node.DOCUMENT_FRAGMENT_NODE: {
            writeChildren(writer, node);
            break;
         }

         case Node.DOCUMENT_TYPE_NODE: {
org.dbxml.Debug.println("can't serialize doctype yet");
            /*
            DocumentType d = (DocumentType)node;
            writer.write("<!DOCTYPE ");
            writer.write(node.getOwnerDocument().getDocumentElement().getNodeName());
            writer.write('>');
            */
            break;
         }

         case Node.ELEMENT_NODE: {
            Element e = (Element)node;
            String n = e.getTagName();

            writer.write('<');
            writer.write(n);

            NamedNodeMap a = e.getAttributes();
            int size = a.getLength();
            for ( int i = 0; i < size; i++ ) {
               Attr att = (Attr)a.item(i);
               writer.write(' ');
               writeNode(writer, att);
            }

            if ( e.hasChildNodes() ) {
               writer.write('>');
               writeChildren(writer, node);
               writer.write("</");
               writer.write(n);
               writer.write('>');
            }
            else
               writer.write(" />");
            break;
         }

         case Node.ATTRIBUTE_NODE:
            Attr a = (Attr)node;
            writer.write(a.getName());
            writer.write("=\"");
            writer.write(a.getValue());
            writer.write("\"");
            break;

         case Node.ENTITY_REFERENCE_NODE: {
org.dbxml.Debug.println("can't serialize entity ref yet");
            break;
         }

         case Node.ENTITY_NODE: {
org.dbxml.Debug.println("can't serialize entity yet");
            break;
         }

         case Node.NOTATION_NODE: {
org.dbxml.Debug.println("can't serialize notation yet");
            break;
         }

         case Node.PROCESSING_INSTRUCTION_NODE: {
            ProcessingInstruction pi = (ProcessingInstruction)node;
            writer.write("<?");
            writer.write(pi.getTarget());
            writer.write(" ");
            writer.write(pi.getData());
            writer.write("?>\n");
            break;
         }

         case Node.TEXT_NODE: {
            char[] value = node.getNodeValue().toCharArray();
            String outval = null;
            int start = 0;
            int len = 0;
            for ( int i = 0; i < value.length; i++ ) {
               switch ( value[i] ) {
                  case '&'  : outval = "&amp;";  break;
                  case '\'' : outval = "&apos;"; break;
                  case '\"' : outval = "&quot;"; break;
                  case '<'  : outval = "&lt;";   break;
                  case '>'  : outval = "&gt;";   break;
                  default   : len++;             break;
               }

               if ( outval != null ) {
                  if ( len > 0 )
                     writer.write(value, start, len);
                  writer.write(outval);
                  start = i+1;
                  len = 0;
                  outval = null;
               }
            }
            if ( len > 0 )
               writer.write(value, start, len);
            break;
         }

         case Node.CDATA_SECTION_NODE: {
            writer.write("<![CDATA[");
            writer.write(node.getNodeValue());
            writer.write("]]>");
            break;
         }

         case Node.COMMENT_NODE: {
            writer.write("<!--");
            writer.write(node.getNodeValue());
            writer.write("-->");
            break;
         }
      }
   }

   private void writeChildren(Writer writer, Node node) throws IOException {
      NodeList l = node.getChildNodes();
      int size = l.getLength();
      for ( int i = 0; i < size; i++ )
         writeNode(writer, l.item(i));
   }

   /**
    * write writes the specified node to the writer as text.
    *
    * @param node The Node to write
    * @param writer The Writer to write to
    */
   public static void write(Node node, Writer writer) throws IOException {
      new TextWriter(node).write(writer);
   }

   /**
    * write writes the node to the writer as text.
    *
    * @param writer The Writer to write to
    */
   public void write(Writer writer) throws IOException {
      try {
         BufferedWriter buf = new BufferedWriter(writer, 4096);
         writeNode(buf, node);
         buf.flush();
      }
      catch ( Exception e ) {
          org.dbxml.Debug.printStackTrace(e);
      }
   }

   /**
    * write writes the specified node to the OutputStream as text.
    *
    * @param node The Node to write
    * @param output The OutputStream to write to
    */
   public static void write(Node node, OutputStream output) throws IOException {
      new TextWriter(node).write(output);
   }

   /**
    * write writes the node to the OutputStream as text.
    *
    * @param output The OutputStream to write to
    */
   public void write(OutputStream output) throws IOException {
      try {
         OutputStreamWriter o = new OutputStreamWriter(output);
         BufferedWriter buf = new BufferedWriter(o, 4096);
         writeNode(buf, node);
         buf.flush();
      }
      catch ( Exception e ) {
          org.dbxml.Debug.printStackTrace(e);
      }
   }

   /**
    * toString returns the node as a String.
    *
    * @param node The Node to convert
    * @return The String value
    */
   public static String toString(Node node) {
      return new TextWriter(node).toString();
   }

   /**
    * toString returns the node as a String.
    *
    * @return The String value
    */
   public String toString() {
      StringWriter writer = new StringWriter();
      try {
         write(writer);
         return writer.toString();
      }
      catch ( Exception e ) {
         org.dbxml.Debug.printStackTrace(e);
         return null;
      }
   }
}

