/*
 * (C) Copyright IBM Corp. 1997-1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import java.io.PrintWriter;

/**
 * A Utility class for output XML document.
 * This class prints extara white-spaces for indentation
 * (<CODE>TXDocument#print()</CODE> method adds no white-spaces).
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 */
public class Format {
    /**
     * You can't make a Format instance.
     */
    private Format() {
    }

    /**
     * Print a document to a stream in XML format.
     * This method adds some spaces in elements.
     *
     * @param dx indent width
     * @param encoding <code>PrintWriter</code>'s encoding name in Java encoding name. It can be <code>null</code>.
     */
    public static void print(TXDocument doc, PrintWriter pw, String encoding, int dx) {
        if (null != doc.getVersion()) {
            pw.print("<?xml version=\"");
            pw.print(doc.getVersion());
            pw.print("\"");
            if (null != doc.getEncoding()) {
                pw.print(" encoding=\"");
                pw.print(doc.getEncoding());
                pw.print("\"");
            }
            if (null != doc.getStandalone()) {
                pw.print(" standalone=\"");
                pw.print(doc.getStandalone());
                pw.print("\"");
            }
            pw.println("?>");
        }
        org.w3c.dom.NodeIterator ni = doc.getChildNodes();
        for (Child ch = (Child)ni.toNextNode();  null != ch;  ch = (Child)ni.toNextNode()) {
            if (ch instanceof TXElement)
                printElement((TXElement)ch, pw, encoding, 0, dx);
            else if (ch instanceof DTD)
                printDTD((DTD)ch, pw, encoding, 0, dx);
            else
                ch.print(pw, encoding);
            pw.println("");
        }
        if (null == doc.rootElement)
            pw.println("<!-- The document has no root element. -->");
        pw.flush();
    }

    /**
     * Print with 2 spaces for indentation.
     */
    public static void print(TXDocument doc, PrintWriter pw, String encoding) {
        print(doc, pw, encoding, 2);
    }

    /**
     * Print with 2 spaces for indentation.
     */
    public static void print(TXDocument doc, PrintWriter pw) {
        print(doc, pw, null, 2);
    }

    /**
     *
     */
    public static void printDTD(DTD dtd, PrintWriter pw, String enc, int ind, int dx) {
        pw.print("<!DOCTYPE ");
        pw.print(dtd.getName());
        pw.print(" ");
        if (null != dtd.getExternalID())  pw.print(dtd.getExternalID().toString()+" ");
        if (dtd.isPrintInternalDTD() && 0 < dtd.getInternalSize()) {
            pw.println("[");
            java.util.Enumeration en = dtd.internalElements();
            while (en.hasMoreElements()) {
                Format.printSpace(pw, ind+dx);
                ((Child)en.nextElement()).print(pw, enc);
                pw.println("");
            }
            Format.printSpace(pw, ind);
            pw.print("]");
        }
        pw.print(">");
    }

    /**
     * Print specified element.
     */
    public static void printElement(TXElement el, PrintWriter pw, String enc, int ind, int dx) {
                                                // Print `<' and name and attributes
        pw.print(el.makeHead(enc).toString());
                                                // Print `>' and contents and end-tag
        Child ch = (Child)el.getFirstChild();
        if (el.isEmpty()) {
            pw.print("/>");

        } else if (1 == el.children.getLength() && TXText.isText(ch)) {
            pw.print(">");
            ch.print(pw, enc);
            pw.print("</");
            pw.print(el.getName());
            pw.print(">");

            
                                        // isPreserveSpace() flag is set
                                        //   when this element has xml:space="preserve"
                                        // or when the parser parsed this document
                                        //      after it was called Parser#setPreserveSpace(true).
        } else if (el.isPreserveSpace()) {
            pw.print(">");
            while (null != ch) {
                if (ch instanceof TXElement)
                    printElement((TXElement)ch, pw, enc, ind+dx, dx);
                else
                    ch.print(pw, enc);
                ch = (Child)ch.getNextSibling();
            }
            pw.print("</");
            pw.print(el.getName());
            pw.print(">");

        } else {
            pw.print(">");
            /*
              0: non-Text
              1: Text
              */
            boolean isprevtext = false;
            for (;  null != ch;  ch = (Child)ch.getNextSibling()) {
                boolean isnowtext = false;
                if (TXText.isText(ch)) {
                    if (((TXText)ch).getIsIgnorableWhitespace()) {
                        isprevtext = false;
                        //ch = (Child)ch.getNextSibling();
                        continue;
                    }
                    isnowtext = true;
                }

                if (!isnowtext && !isprevtext)  indent(pw, ind+dx);
                if (ch instanceof TXElement)
                    printElement((TXElement)ch, pw, enc, ind+dx, dx);
                else
                    ch.print(pw, enc);

                isprevtext = isnowtext;
            }
            if (!isprevtext)  indent(pw, ind);
            pw.print("</");
            pw.print(el.getName());
            pw.print(">");
        }
    }

    /**
     * Print <var>n</var> spaces.
     */
    public static void printSpace(java.io.PrintWriter pw, int n) {
        for (int i = 0;  i < n;  i ++)
            pw.print(" ");
    }

    /**
     * Print a return character and <code>printSpace(pw,n)</code>.
     */
    public static void indent(java.io.PrintWriter pw, int n) {
        pw.println("");
        for (int i = 0;  i < n;  i ++)
            pw.print(" ");
    }

}
