/*
 * Copyright 2006 Mask@ Project.
 * 
 * Licensed 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.
 */
package org.maskat.framework.eventdef.xml;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.digester.Digester;
import org.maskat.framework.eventdef.EventRef;
import org.maskat.framework.eventdef.Layout;
import org.maskat.framework.eventdef.RemoteUrl;
import org.maskat.ide.property.BindProperty;
import org.maskat.ide.property.ComponentProperty;
import org.maskat.ide.property.EventProperty;
import org.maskat.ide.property.HeaderProperty;
import org.maskat.ide.property.LayoutProperty;
import org.maskat.ide.property.ParamProperty;
import org.maskat.ide.property.ResultProperty;
import org.maskat.ide.property.SourceProperty;
import org.maskat.ide.property.TargetProperty;
import org.maskat.xml.digester.MaskatDigester;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

public class EventDefParser {
	public static Layout parse(InputStream input) throws IOException, SAXException {
		Layout result = null;
		Digester digester = new MaskatDigester();

		digester.setValidating(false);
		digester.setNamespaceAware(true);

		digester.addObjectCreate("eventDef", null, LayoutProperty.class);
		digester.addSetProperties("eventDef");

		digester.addCallMethod("*/desc", "addDesc", 0);

		digester.addObjectCreate("*/remoteUrl", RemoteUrl.class);
		digester.addSetProperties("*/remoteUrl", "url", "url");
		digester.addSetNext("*/remoteUrl", "addChild", RemoteUrl.class.getName());

		digester.addObjectCreate("*/header", HeaderProperty.class);
		digester.addSetProperties("*/header");
		digester.addSetNext("*/header", "addChild", HeaderProperty.class.getName());

		digester.addObjectCreate("*/eventDef/eventRef", EventRef.class);
		digester.addSetProperties("*/eventDef/eventRef", "id", "refid");
		digester.addSetNext("*/eventDef/eventRef", "addChild", EventRef.class.getName());

		digester.addObjectCreate("*/eventDef/event", EventProperty.class);
		digester.addSetProperties("*/eventDef/event");
		digester
				.addSetNext("*/eventDef/event", "addChild", EventProperty.class.getName());

		digester.addObjectCreate("*/eventDef/component", ComponentProperty.class);
		digester.addSetProperties("*/eventDef/component");
		digester.addSetNext("*/eventDef/component", "addChild", ComponentProperty.class
				.getName());

		digester.addObjectCreate("*/eventDef/component/event", EventProperty.class);
		digester.addSetProperties("*/eventDef/component/event", "id", "id");
		digester.addSetNext("*/eventDef/component/event", "addChild", EventProperty.class
				.getName());

		digester.addObjectCreate("*/param", ParamProperty.class);
		digester.addSetProperties("*/param");
		digester.addSetNext("*/param", "addChild", ParamProperty.class.getName());

		digester.addObjectCreate("*/param/source", SourceProperty.class);
		digester.addSetProperties("*/param/source");
		digester.addSetNext("*/param/source", "addChild", SourceProperty.class.getName());

		digester.addObjectCreate("*/bind", BindProperty.class);
		digester.addSetProperties("*/bind");
		digester.addSetNext("*/bind", "addChild", BindProperty.class.getName());

		digester.addObjectCreate("*/result", ResultProperty.class);
		digester.addSetProperties("*/result");
		digester.addSetNext("*/result", "addChild", ResultProperty.class.getName());

		digester.addObjectCreate("*/result/target", TargetProperty.class);
		digester.addSetProperties("*/result/target");
		digester
				.addSetNext("*/result/target", "addChild", TargetProperty.class.getName());

		result = (Layout) digester.parse(input);

		result.expandEventRefToEvent();

		return result;
	}

	/** スキーマ生成するためのインスタンスに転換用のxslt、各要素のテキスト値としては要素のタイプです。例えば：&lt;data&gt;xs:int&lt;/data&gt; */
	public static final String EVENTDEF_XSLT_FOR_SCHEMA_GEN = "org/maskat/framework/eventdef/xml/ruleSplitByTele.xsl";

	/** スキーマ検証するためのインスタンスに転換用のxslt、各要素のテキスト値としては要素の値です。例えば：&lt;data&gt;1&lt;/data&gt; */
	public static final String EVENTDEF_XSLT_FOR_INSTANCE = "org/maskat/framework/eventdef/xml/toTeleInstance.xsl";

	/**
	 * イベント定義XMLから電文XMLのインスタンスを生成する
	 * 
	 * @param stream
	 *            イベント定義XMLのInputStream
	 * @param name
	 *            転換用xsltのパス
	 * @return ノードリスト、SendTeleとRecvTeleノードのリスト
	 * @throws TransformerException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 * @throws IOException
	 */
	public static NodeList genTeleInstanceFromEventDef(InputStream stream, String name)
			throws TransformerException, ParserConfigurationException, SAXException,
			IOException {

		// TransformerFactoryインスタンスを取得
		TransformerFactory factory = TransformerFactory.newInstance();

		// XSLファイルからtranceformerを取得
		InputStream ruleStream = EventDefParser.class.getClassLoader()
				.getResourceAsStream(name);
		Transformer transformer = factory.newTransformer(new StreamSource(ruleStream));
		// 出力するエンコーディングを設定
		transformer.setOutputProperty("encoding", "UTF-8");

		// 結果を格納する用DOMResultを作る
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = dbFactory.newDocumentBuilder();
		Document doc = builder.parse(new ByteArrayInputStream("<result></result>"
				.getBytes()));
		DOMResult dr = new DOMResult(doc.getDocumentElement());

		XMLReader rdr = XMLReaderFactory.createXMLReader();
		rdr.setEntityResolver(new EntityResolver() {
			public InputSource resolveEntity(String publicId, String systemId)
					throws SAXException, IOException {
				return new InputSource(new StringReader(""));
			}
		});

		// XMLファイルをXSLTで変換して出力
		transformer.transform(new SAXSource(rdr, new InputSource(stream)), dr);
		return dr.getNode().getChildNodes();
	}
}
