/**
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.kinako.logic;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.logical_paradox.common.charset.CharsetUtils;
import org.logical_paradox.kinako.constant.ApConstant;
import org.logical_paradox.kinako.object.SearchCondition;
import org.logical_paradox.phalanx.binding.resultset.ResultSet;

/**
 * 全文検索システムから受信した検索結果のXMLをHTMLに変換する
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.6 $
 */
public class SearchResultTranslator {
	/** ロガー */
	private static final Log log = LogFactory.getLog(SearchResultTranslator.class);

	/** HTML変換器のインスタンス */
	private static final SearchResultTranslator translator = new SearchResultTranslator();
	/** 検索結果XMLのアンマーシャラー */
	private Unmarshaller resultSetUmarshaller;

	/**
	 * コンストラクタ
	 */
	private SearchResultTranslator() {
		// アンマーシャラー
		JAXBContext resultSetCtx;
		try {
			resultSetCtx = JAXBContext.newInstance(ApConstant.PHALANX_JAXB_PACKAGE_NAME + ".resultset");
			resultSetUmarshaller = resultSetCtx.createUnmarshaller();
//			resultSetUmarshaller.setValidating(true);
		} catch (JAXBException e) {
			log.error("アンマーシャラーの生成中に例外が発生しました：", e);
		}
	}
	/**
	 * シングルトンのインスタンスを返す
	 * @return HTML変換器のインスタンス
	 */
	public static SearchResultTranslator getInstance() {
		return translator;
	}
	/**
	 * 入力ストリームから受信したXMLを，指定されたXSLに従ってHTMLに変換し，出力ストリームを流す
	 * 全て受信し終わったとしても，入力ストリームも出力ストリームも勝手に閉じないため，<br>
	 * このメソッドを呼び出す側がストリームのクローズ処理を管理する必要がある
	 * @param ostream 出力ストリーム
	 * @param istream 入力ストリーム
	 * @param xslt 変換ルール
	 * @param inputEncoding 入力エンコーディング
	 * @param outputEncoding 出力エンコーディング
	 * @param condition 検索条件 
	 * @return 検索結果の総数
	 */
	public int output(OutputStream ostream, InputStream istream, String xslt, String inputEncoding, SearchCondition condition) throws IOException, TransformerException, JAXBException {
		// 一度データソースからXMLテキストを読み込む
		BufferedReader br = new BufferedReader(new InputStreamReader(istream, inputEncoding));
		StringBuffer sb = new StringBuffer();
		String line = null;
		while((line = br.readLine()) != null) {
			sb.append(line);
		}

		// 入力ストリームからXMLを取得する
		String xmlDocument = sb.toString();

		String originalEncodingXml = CharsetUtils.encode(inputEncoding, xmlDocument);
		InputStreamReader ir = new InputStreamReader(new ByteArrayInputStream(xmlDocument.getBytes()));
		BufferedReader xmlReader = new BufferedReader(ir);

		// for JDK 1.5.0
		System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");

		TransformerFactory factory = TransformerFactory.newInstance();
		Transformer transformer = factory.newTransformer(new StreamSource(xslt));
		transformer.setParameter(ApConstant.XSLT_PARAM_KEYWORD, condition.getKeyword());
		// 出力エンコーディング
		transformer.setOutputProperty(OutputKeys.ENCODING, System.getProperty("file.encoding"));

		// 変換して出力
		transformer.transform(new StreamSource(xmlReader), new StreamResult(ostream));

		// XMLドキュメントをオブジェクトに変換する
		ResultSet rs = getResultSet(originalEncodingXml.getBytes("ISO-8859-1"));

		return rs.getHits();
	}
	/**
	 * 検索結果XMLをオブジェクトに変換する
	 * @param xml 全文検索apサーバーから返却されたXMLドキュメント
	 * @return バインドされたオブジェクト
	 */
	protected ResultSet getResultSet(byte[] xml) throws JAXBException {
		ResultSet rs = (ResultSet)resultSetUmarshaller.unmarshal(new ByteArrayInputStream(xml));
		return rs;
	}
}
