/*
 * $Id: MySQLSelect.java,v 1.6 2006/02/26 12:24:58 akabane Exp $
 * LOGICAL-PARADOX.ORG
 * Copyright (C)2005 satoshi akabane(akabane@logical-paradox.org)
 *
 */
package org.logical_paradox.koike.rss.tools.itb.search.impl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

import org.apache.commons.codec.binary.Base64;
import org.logical_paradox.koike.rss.tools.DataSourceException;
import org.logical_paradox.koike.rss.tools.itb.BrowserProperties;
import org.logical_paradox.koike.rss.tools.itb.search.Query;
import org.logical_paradox.koike.rss.tools.itb.search.QueryRecord;
import org.logical_paradox.koike.rss.tools.itb.search.Select;

/**
 * MySQLに接続するタイプのレコードセレクタ
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.6 $
 */
public class MySQLSelect implements Select {
	/** JDBCドライバクラス */
	public static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
	/** 索引語検索クエリー */
	public static final String INDEX_TERM_QUERY = "SELECT term, docno, location FROM inverted_index";
	/** 索引語件数取得クエリー */
	public static final String COUNT_TERM_QUERY = "SELECT count(*) as c FROM inverted_index";

	/** JDBC接続 */
	private Connection con;

	/** base64 encoder/decoder */
	private Base64 base64 = new Base64();

	/**
	 * データソースとの接続をオープンする．
	 * @param connstr 接続文字列
	 * @throws DataSourceException データソースとの接続に失敗
	 */
	public void open(String connstr) throws DataSourceException {
		try {
			Class.forName(JDBC_DRIVER);
			con = DriverManager.getConnection(connstr);
		} catch(Exception e) {
			throw new DataSourceException(e);
		}
	}
	/**
	 * データソースとの接続を閉じる．
	 * @throws DataSourceException データソースとの切断に失敗
	 */
	public void close() throws DataSourceException {
		if(con != null) {
			try {
				con.close();
			} catch(Exception e) {
				throw new DataSourceException(e);
			}
			con = null;
		}
	}

	/**
	 * 検索結果を返す．
	 * @param q クエリー
	 * @return 検索結果レコード(あまりに多い場合，適当にカットされる)
	 * @throws DataSourceException データソースとの接続に失敗
	 */
	public QueryRecord[] executeQuery(Query q) throws DataSourceException {
		PreparedStatement pstmt = null;

		if(q.getTerm() != null && q.getTerm().trim().length() > 0) {
			try {
				String term = new String(base64.encode(q.getTerm().getBytes()));
				pstmt = con.prepareStatement(INDEX_TERM_QUERY + " WHERE term=?");
				pstmt.setString(1, term);
			} catch(SQLException e) {
				throw new DataSourceException(e);
			}
		} else {
			int from = q.getFrom() == Query.NaN ? 0 : q.getFrom();
			int to = q.getTo() == Query.NaN ? BrowserProperties.getIntegerProperty("select.param.rows").intValue() : q.getTo();

			// 新しくクエリーを設定する
			q.setFrom(from);
			q.setTo(to);
			try {
				pstmt = con.prepareStatement(INDEX_TERM_QUERY + " LIMIT ?,?");
				pstmt.setInt(1, from);
				pstmt.setInt(2, to - from);
			} catch(SQLException e) {
				throw new DataSourceException(e);
			}
		}

		ResultSet rs = null;
		ArrayList<QueryRecord> results = new ArrayList<QueryRecord>();
		try {
			rs = pstmt.executeQuery();
			while(rs.next()) {
				QueryRecord qr = new QueryRecord();
				qr.setTerm(new String(base64.decode(rs.getString("term").getBytes())));
				qr.setLocation(rs.getString("location"));
				qr.setDocno("" + rs.getLong("docno"));
				results.add(qr);
			}
		} catch(SQLException e) {
			try {
				if(rs != null) {
					rs.close();
				}
				if(pstmt != null) {
					pstmt.close();
				}
			} catch(SQLException e1) {
			}
		} finally {
			rs = null;
			pstmt = null;
		}

		return (QueryRecord[])results.toArray(new QueryRecord[0]);
	}

	/**
	 * 指定条件でヒットする検索結果数を返す．
	 * @param q クエリー
	 * @return 件数
	 * @throws DataSourceException データソースとの接続に失敗
	 */
	public int countQuery(Query q) throws DataSourceException {
		ResultSet rs = null;
		Statement st = null;
		int count = 0;

		try {
			st = con.createStatement();
			rs = st.executeQuery(COUNT_TERM_QUERY);

			if(rs.next()) {
				count = rs.getInt("c");
			}
		} catch(SQLException e) {
			try {
				if(rs != null) {
					rs.close();
				}
				if(st != null) {
					st.close();
				}
			} catch(SQLException e1) {
			} finally {
				rs = null;
				st = null;
			}
		}

		return count;
	}

}
