/*
 * blanco Framework
 * Copyright (C) 2004-2005 IGA Tosiki
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.sax;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;

import javax.xml.transform.sax.SAXResult;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import blanco.commons.util.BlancoJavaSourceUtil;
import blanco.commons.util.BlancoNameAdjuster;
import blanco.ig.expander.ClassExpander;
import blanco.ig.expander.Scope;
import blanco.ig.expander.Type;
import blanco.ig.expander.field.FieldExpander;
import blanco.ig.expander.method.MethodExpander;
import blanco.ig.generator.GeneratorSetting;
import blanco.ig.generator.ImplementGenerator;
import blanco.sax.util.BlancoSaxUtil;

/**
 * 解析用XMLを入力して具象化SAXライターのテストクラスを生成します。<br>
 * このクラスでは、ConcreteSaxSerializerを生成します。<br>
 * <br>
 * 入力する解析用XMLファイルは、恣意的なものを準備します。現時点では かなり限定的なものになります。<br>
 * 仮定1: 解析用XMLのアトリビュートはすべて指定されて来るものと仮定します。<br>
 * 仮定2: 異なる階層において同じ名前のエンティティが存在しないものと仮定します。<br>
 * 参考:http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/org/xml/sax/ContentHandler.html<br>
 * 
 * @author IGA Tosiki
 */
public class BlancoSaxXml2HandlerTest {
    /**
     * 登場したエレメントすべてを記憶します。<br>
     * 2度目に登場したエレメントを無視するためにも利用されます。
     */
    private final Hashtable totalElementHashtable = new Hashtable();

    /**
     * テスト用のエントリポイント
     * 
     * @param args
     */
    public static void main(String[] args) {
        try {
            new BlancoSaxXml2HandlerTest().process(new File(
                    BlancoSaxXml2Handler.MAIN_INPUT_FILENAME), "blanco",
                    "sample", "AaaHandler");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * ソースコード生成処理を実行します。
     * 
     * @param sourceFile
     * @param targetDirectory
     * @param packageName
     * @param className
     * @throws IOException
     */
    public void process(final File sourceFile, final String targetDirectory,
            final String packageName, final String className)
            throws IOException {
        // Javaソースコードを生成のための設定
        GeneratorSetting setting = new GeneratorSetting();
        // 出力先ディレクトリを設定します。
        setting.setWorkDirectory(targetDirectory);
        ImplementGenerator implementGenerator = new ImplementGenerator(setting);
        // 作成したクラスを登録します。
        implementGenerator.addTest(getClassExpander(sourceFile, packageName,
                className));
        // 実際にソースコード生成を実行します。
        implementGenerator.generate();
        // System.out.println("ソースコード生成が完了しました。");
    }

    /**
     * 生成後のクラス展開を実行します。
     * 
     * @param sourceFile
     * @param packageName
     * @param className
     * @return
     * @throws IOException
     */
    private ClassExpander getClassExpander(final File sourceFile,
            final String packageName, final String className)
            throws IOException {
        BufferedReader readerXml = null;
        StringBuffer xmlImageWrk = new StringBuffer();
        try {
            readerXml = new BufferedReader(new InputStreamReader(
                    new FileInputStream(sourceFile), "UTF-8"));
            for (;;) {
                int iRead = readerXml.read();
                if (iRead < 0) {
                    break;
                }
                if (iRead == '\r') {
                    continue;
                }
                xmlImageWrk.append((char) iRead);
            }
        } finally {
            if (readerXml != null) {
                readerXml.close();
            }
        }
        final String xmlImage = xmlImageWrk.toString();

        return new ClassExpander(new Type(packageName, className + "Test")) {
            protected void expandClassStruct() {
                addFileComment(className + "具象化SAXクラステストクラス<br>");
                getJavaDoc().addLine(className + "具象化SAXクラステストクラス<br>");
                getJavaDoc()
                        .addLine("SAXを利用したXML読み込みをラッピングするクラスのテストクラスです。<br>");
                getJavaDoc().addLine(
                        "このクラスは解析用XMLファイルを入力として具象化SAXクラスとして生成されました。<br>");
                getJavaDoc().addLine("このソースコードはblancoIgにより機械的に自動生成されています。");
                setSuperClass(new Type("junit.framework.TestCase"));

                addMethod(new MethodExpander("testHandler") {
                    // メソッドのシグニチャ指定
                    public void setupSignature() {
                        addUsingType(new Type("java.io.BufferedInputStream"));
                        addUsingType(new Type("java.io.ByteArrayInputStream"));
                        addUsingType(new Type("java.io.InputStream"));
                        addUsingType(new Type("javax.xml.transform.Transformer"));
                        addUsingType(new Type(
                                "javax.xml.transform.TransformerFactory"));
                        addUsingType(new Type(
                                "javax.xml.transform.sax.SAXResult"));
                        addUsingType(new Type(
                                "javax.xml.transform.stream.StreamSource"));
                        addUsingType(new Type("org.xml.sax.SAXException"));

                        addException(new Type("java.lang.Exception"));

                        // フィールド変数を宣言します。
                        FieldExpander field1 = new FieldExpander(new Type(
                                "java.lang.String"), "fXmlImage") {
                            protected Scope getScope() {
                                return Scope.PUBLIC;
                            }

                            protected boolean isStatic() {
                                return true;
                            }

                            protected boolean isFinal() {
                                return true;
                            }
                        };
                        field1.setLiteralValue(BlancoJavaSourceUtil
                                .escapeStringAsJavaSource(xmlImage));
                        field1.getJavaDoc().addLine("テスト用のXMLデータです<br>");
                        field1.getJavaDoc().addLine(
                                "これがXML構造を解析する際の入力データでもあります。");
                        addField(field1);
                    }

                    public void implement() {
                        getData().addLine(
                                "SAXResult result = new SAXResult(new "
                                        + className + "() {");

                        SAXResult result = new SAXResult(new ContentHandler() {
                            public void endDocument() throws SAXException {
                                getData().addLine("});");
                                getData().addLine("");

                                getData().addLine(
                                        "InputStream inStream = null;");
                                getData().addLine("try {");
                                getData()
                                        .addLine(
                                                "inStream = new BufferedInputStream(new ByteArrayInputStream(fXmlImage.getBytes()));");
                                getData()
                                        .addLine(
                                                "TransformerFactory tf = TransformerFactory.newInstance();");
                                getData()
                                        .addLine(
                                                "Transformer transformer = tf.newTransformer();");
                                getData()
                                        .addLine(
                                                "transformer.transform(new StreamSource(inStream), result);");
                                getData().addLine("} finally {");
                                getData().addLine("if (inStream != null) {");
                                getData().addLine("inStream.close();");
                                getData().addLine("}");
                                getData().addLine("}");
                            }

                            public void startDocument() throws SAXException {
                                getData()
                                        .addLine(
                                                "public void startDocument() throws SAXException {");
                                getData().addLine("}");
                                getData().addLine("");
                                getData()
                                        .addLine(
                                                "public void endDocument() throws SAXException {");
                                getData().addLine("}");
                            }

                            public void characters(char[] ch, int start,
                                    int length) throws SAXException {
                            }

                            public void ignorableWhitespace(char[] arg0,
                                    int arg1, int arg2) throws SAXException {
                            }

                            public void endPrefixMapping(String arg0)
                                    throws SAXException {
                            }

                            public void skippedEntity(String arg0)
                                    throws SAXException {
                            }

                            public void setDocumentLocator(Locator arg0) {
                            }

                            public void processingInstruction(String arg0,
                                    String arg1) throws SAXException {
                            }

                            public void startPrefixMapping(String arg0,
                                    String arg1) throws SAXException {
                            }

                            public void startElement(String arg0, String arg1,
                                    String qName, Attributes atts)
                                    throws SAXException {
                                if (totalElementHashtable.get(qName) != null) {
                                    // すでに登場したエレメントです。
                                    // 処理はスキップします。
                                    return;
                                }
                                totalElementHashtable.put(qName, "found");

                                String lineStatement = "public void startElement"
                                        + BlancoNameAdjuster.toClassName(qName)
                                        + "(String uri, String localName, String qName";
                                String attrList = "";
                                for (int index = 0; index < atts.getLength(); index++) {
                                    lineStatement += ", ";
                                    lineStatement += "String attr"
                                            + BlancoNameAdjuster
                                                    .toClassName(atts
                                                            .getQName(index));
                                    attrList += ": "
                                            + atts.getQName(index)
                                            + "[\" + attr"
                                            + BlancoNameAdjuster
                                                    .toClassName(atts
                                                            .getQName(index))
                                            + " + \"]";
                                }
                                lineStatement += ") throws SAXException {";
                                getData().addLine(lineStatement);
                                getData().addLine(
                                        "System.out.println(\"start: " + qName
                                                + attrList + "\");");
                                getData().addLine("}");

                                getData().addLine("");

                                getData()
                                        .addLine(
                                                "public void endElement"
                                                        + BlancoNameAdjuster
                                                                .toClassName(qName)
                                                        + "(String uri, String localName, String qName"
                                                        + ") throws SAXException {");
                                getData().addLine(
                                        "System.out.println(\"end: " + qName
                                                + "\");");
                                getData().addLine("}");
                                getData().addLine("");
                                getData()
                                        .addLine(
                                                "public void characters"
                                                        + BlancoNameAdjuster
                                                                .toClassName(qName)
                                                        + "(char[] ch, int start, int length) throws SAXException {");
                                getData()
                                        .addLine(
                                                "System.out.println(\"characters: "
                                                        + qName
                                                        + "[\" + new String(ch, start, length) + \"]\");");
                                getData().addLine("}");
                                getData().addLine("");
                                getData()
                                        .addLine(
                                                "public void ignorableWhitespace"
                                                        + BlancoNameAdjuster
                                                                .toClassName(qName)
                                                        + "(char[] ch, int start, int length) throws SAXException {");
                                getData().addLine("}");
                                getData().addLine("");
                            }

                            public void endElement(String arg0, String arg1,
                                    String arg2) throws SAXException {
                            }
                        });

                        BlancoSaxUtil.doTransform(sourceFile, result);
                    }
                });
            }
        };
    }
}
