/*
 * (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.sample;

import com.ibm.xml.parser.Child;
import com.ibm.xml.parser.DTD;
import com.ibm.xml.parser.Format;
import com.ibm.xml.parser.MIME2Java;
import com.ibm.xml.parser.Parent;
import com.ibm.xml.parser.Parser;
import com.ibm.xml.parser.Source;
import com.ibm.xml.parser.Stderr;
import com.ibm.xml.parser.TXDocument;
import com.ibm.xml.parser.TXElement;
import com.ibm.xml.parser.TXText;
import com.ibm.xml.xpointer.XPointer;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Locale;
import java.util.StringTokenizer;

/**
 * XML Syntax Checker.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 */
public class trlx {
    private trlx() {
    }
    
    private static void reportMemory(Runtime rt) {
        long total = rt.totalMemory();
        long free = rt.freeMemory();
        long using = total-free;
        System.err.println(free+"/"+total+": use "+using+" Byte");
    }

    public static void main(String[] argv) {
        Locale.setDefault(new Locale(System.getProperty("user.language", "en"),
                                     System.getProperty("user.region", "")));
        int ret = process(argv);

        System.exit(ret);
    }

    static boolean s_reportmemory = false;
    static Runtime s_rt = Runtime.getRuntime();

    public static int process(String[] argv) {
        try {
            String fname = null;
            boolean isprint = false;
            boolean isformatprint = false;
            String encname = null;
            boolean isprintclass = false;
            boolean isprintxpointer = false;
            boolean isallowj = false;
            boolean iskeepcomment = true;
            boolean donamespace = false;
            boolean warnnoxml = false;
            boolean dodtd = false;
            int loop = 0;

            for (int i = 0;  i < argv.length;  i ++) {
                if ('-' != argv[i].charAt(0)) {
                    fname = argv[i];

                } else if ("-memory".equals(argv[i])) {
                    s_reportmemory = true;
                    System.err.print("Initial free/total memory:\n\t");
                    reportMemory(s_rt);

                } else if ("-print".equals(argv[i]) || "-d".equals(argv[i])) {
                    isprint = true;

                } else if ("-format".equals(argv[i])
                           || "-print2".equals(argv[i])
                           || "-d2".equals(argv[i])) {
                    isformatprint = true;

                } else if ("-class".equals(argv[i])) {
                    isprintclass = true;

                } else if ("-xpointer".equals(argv[i])) {
                    isprintxpointer = true;

                } else if ("-dtd".equals(argv[i])) {
                    dodtd = true;

                } else if ("-n".equals(argv[i]) || "-t".equals(argv[i])) {
                    if (i+1 < argv.length) {
                        int v = Integer.parseInt(argv[++i]);
                        if (0 >= v) {
                            System.err.println("-n/-t requires a number more than 0.");
                        } else
                            loop = v;
                    } else {
                        System.err.println("-n/-t requires a number.");
                    }

                } else if ("-outencoding".equals(argv[i])) {
                    if (i+1 < argv.length) {
                        encname = argv[++i];
                    } else {
                        System.err.println("-outencoding requires an encoding name.");
                    }

                } else if ("-allowjavaencoding".equals(argv[i])) {
                    isallowj = true;

                } else if ("-namespace".equals(argv[i])) {
                    donamespace = true;

                } else if ("-removecomment".equals(argv[i])) {
                    iskeepcomment = false;

                } else if ("-warnnoxml".equals(argv[i])) {
                    warnnoxml = true;

                } else {
                    System.err.println("Warning: Unknown option: "+argv[i]);
                }
            }

            TXDocument doc = null;
            DTD dtd = null;
            if (0 == loop) {
                if (null == fname) {
                    System.err.println("XML Syntax Checker");
                    System.err.println("Usage:\n\tjava trlx [options] <URL or filename>");
                    System.err.println("Options:");
                    System.err.println("\t-d\t\tPrint parsed document in XML format");
                    System.err.println("\t-format\t\tPrint parsed document in XML format with formatting");
                    System.err.println("\t-outencoding mimename\tSpecify encoding when print");
                    System.err.println("\t-namespace\tEnable namespace feature");
                    System.err.println("\t-n <n>\t\tParse n times and print time");
                    System.err.println("\t-memory\t\tReport memory usage");
                    System.err.println("External-DTD parsing:");
                    System.err.println("\t-dtd");
                    System.err.println("\t-d\tPrint parsed DTD");
                    System.err.println("");
                    System.err.println("To know other options, See the source :-)");
                    return 1;
                }
                Stderr se = new Stderr(fname);
                Source src = se.getInputStream(fname, null, Stderr.file2URL(fname).toString());

                Parser pc = new Parser(fname, se, se);
                pc.setAllowJavaEncodingName(isallowj);
                pc.setKeepComment(iskeepcomment);
                pc.setWarningNoDoctypeDecl(false);
                pc.setProcessNamespace(donamespace);
                pc.setWarningNoXMLDecl(warnnoxml);
                if (dodtd) {
                    dtd = pc.readDTDStream(src);
                    if (isprint) {
                        if (null == encname) {
                            dtd.printExternal(new PrintWriter(System.out), null);
                        } else {
                            String enc = MIME2Java.convert(encname);
                            if (null == enc) {
                                System.err.println("Encoding `"+encname+"' is not supported.");
                                dtd.printExternal(new PrintWriter(System.out), null);
                            } else {
                                dtd.setEncoding(encname);
                                dtd.printExternal(new PrintWriter(new OutputStreamWriter(System.out, enc)),
                                                  enc);
                            }
                        }
                    }
                    return 0;
                } else {
                    doc = pc.readStream(src);
                }
                pc = null;

                if (null == doc) {
                    System.err.println("No document");
                } else if (isformatprint) {
                    if (null == encname) {
                        Format.print(doc, new PrintWriter(System.out));
                    } else {
                        String enc = MIME2Java.convert(encname);
                        if (null == enc) {
                            System.err.println("Encoding `"+encname+"' is not supported.");
                            Format.print(doc, new PrintWriter(System.out));
                        } else {
                            Format.print(doc, new PrintWriter(new OutputStreamWriter(System.out, enc)),
                                      enc, 2);
                        }

                    }
                } else if (isprint) {
                    if (null == encname) {
                        doc.print(new PrintWriter(new OutputStreamWriter(System.out)));
                    } else {
                        String enc = MIME2Java.convert(encname);
                        if (null == enc) {
                            System.err.println("Encoding `"+encname+"' is not supported.");
                            doc.print(new PrintWriter(new OutputStreamWriter(System.out)));
                        } else {
                            doc.print(new PrintWriter(new OutputStreamWriter(System.out, enc)),
                                      enc);
                        }

                    }
                }

                if (isprintclass)
                    printClassTree(doc, new PrintWriter(new OutputStreamWriter(System.out)), 0);
                if (isprintxpointer)
                    printXPointers(doc, new PrintWriter(new OutputStreamWriter(System.out)), 0);

            } else {
                long total = 0;
                s_rt.gc();
                s_rt.runFinalization();
                s_rt.gc();
                for (int i = 0;  i < loop;  i ++) {
                    Parser pc = new Parser(fname);
                    long t = System.currentTimeMillis();
                    pc.setAllowJavaEncodingName(isallowj);
                    pc.setKeepComment(iskeepcomment);
                    pc.setWarningNoDoctypeDecl(false);
                    pc.setProcessNamespace(donamespace);
                    pc.setWarningNoXMLDecl(warnnoxml);
                    pc.readStream(new FileInputStream(fname));
                    t = System.currentTimeMillis()-t;
                    System.err.println(t+" [msec]");
                    s_rt.gc();
                    s_rt.runFinalization();
                    s_rt.gc();
                    total += t;
                }
                System.err.println("Total: "+total+" [msec],  Average: "+(total/loop)+" [msec]");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (s_reportmemory) {
            System.err.print("Free/total memory after processing:\n\t");
            reportMemory(s_rt);
            System.err.println("Garbage Collecting ...");
            System.err.flush();
            s_rt.gc();
            s_rt.runFinalization();
            s_rt.gc();
            System.err.print("Free/total memory after gc:\n\t");
            reportMemory(s_rt);
            System.err.flush();
        }
        return 0;
    }

    public static String getLastName(String cl) {
        String ret = cl;
        StringTokenizer st = new StringTokenizer(cl, " .");
        while (st.hasMoreTokens())
            ret = st.nextToken();
        return ret;
    }
    public static void printClassTree(Parent par, PrintWriter pw, int indent) {
        org.w3c.dom.NodeIterator ne = par.getChildNodes();
        Child ch;
        while (null != (ch = (Child)ne.toNextNode())) {
            Format.printSpace(pw, indent);
            pw.print(getLastName(ch.getClass().toString()));
            pw.println(" \""+TXText.makePrintable(ch.getText())+"\"");
            if (ch instanceof Parent) {
                printClassTree((Parent)ch, pw, indent+2);
            }
        }
        pw.flush();
    }

    public static void printXPointers(Parent par, PrintWriter pw, int indent) {
        org.w3c.dom.NodeIterator ne = par.getChildNodes();
        Child ch;
        while (null != (ch = (Child)ne.toNextNode())) {
            if (!(par instanceof TXDocument) || ch instanceof TXElement) {
                Format.printSpace(pw, indent);
                pw.print(getLastName(ch.getClass().toString()));
                pw.print("\t");
                pw.println(ch.makeXPointer());
                if (ch instanceof Parent) {
                    printXPointers((Parent)ch, pw, indent+2);
                }
            }
        }
        pw.flush();
    }
}
