/*  OpenOlap viewer
 *  パッケージ名：openolap.viewer.dao
 *  ファイル：CellDataSQL.java
 *  説明：セルデータ取得時に使用するSQLをあらわすクラスです。
 *
 *  作成日: 2004/02/01
 */
package openolap.viewer.dao;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.StringTokenizer;

import openolap.viewer.MeasureMember;
import openolap.viewer.Report;
import openolap.viewer.common.CommonSettings;
import openolap.viewer.common.Constants;
import openolap.viewer.common.StringUtil;

/**
 *  クラス：CellDataSQL
 *  説明：セルデータ取得時に使用するSQLをあらわすクラスです。
 */
public class CellDataSQL {

	// ********** インスタンス変数 **********

	/** 列エッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリスト */
	private ArrayList colFactKeyColumnList = new ArrayList();

	/** 行エッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリスト */
	private ArrayList rowFactKeyColumnList = new ArrayList();

	/** ページエッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリスト */
	private ArrayList pageFactKeyColumnList = new ArrayList();


	/** Factテーブル名 */
	private String factTableName = null;


	/** 
	 * 列に配置された軸のWHERE節をあらわすオブジェクト。
	 * その軸をあらわすFactテーブルのキーカラムをKeyとし、取得対象となるメンバーキーリストをValueに持つ。
	 */
	private LinkedHashMap colWhereClauseMap = new LinkedHashMap();

	/** 
	 * 行に配置された軸のWHERE節をあらわすオブジェクト。
	 * その軸をあらわすFactテーブルのキーカラムをKeyとし、取得対象となるメンバーキーリストをValueに持つ。
	 */
	private LinkedHashMap rowWhereClauseMap = new LinkedHashMap();

	/** 
	 * ページに配置された軸のWHERE節をあらわすオブジェクト。
	 * その軸をあらわすFactテーブルのキーカラムをKeyとし、取得対象となるメンバーキーリストをValueに持つ。
	 */
	private LinkedHashMap pageWhereClauseMap = new LinkedHashMap();

	// メジャーがページエッジにあるときのデフォルトメンバーキー(UName)
	private String measureDefaultMember = null;


	// ********** static メソッド **********

	/**
	 * CellDataSQLオブジェクトを生成する。
	 * @param report レポートオブジェクト
	 * @param conn Connecionオブジェクト
	 * @param items 各エッジの軸IDリスト
	 * @param selectKeys データ取得対象となる列・行ヘッダのKey属性リスト
	 *		  selectKeys[0]：列ヘッダのKey属性リスト文字列
	 *		  selectKeys[1]：行ヘッダのKey属性リスト文字列
	 * @param formatValue 値にメジャーメンバー毎に設定されたMeasureMemberTypeの書式を適用するならtrue、単位のみをそろえる書式を適用するならばfalse
	 * @param commonSettings アプリケーションの共通設定をあらわすオブジェクト
	 * @return CellDataSQLオブジェクト
	 */
	public static CellDataSQL getSelectReportDataSQL(Report report, Connection conn, Object[] items, Object[] selectKeys, boolean formatValue, CommonSettings commonSettings) {

		CellDataSQL cellDataSQL = new CellDataSQL();

		int i;
		int j;
		int k;
		StringTokenizer st;

		// ファクトテーブル名を設定
		cellDataSQL.setFactTableName(DAOFactory.getDAOFactory().getCubeDAO(null).getFactTableName(report.getCube().getCubeSeq()));

		
		String[] edgeAxisInfoList;		// あるエッジの軸情報を格納するオブジェクト
		String axisID;					// 軸ID
		String tmpItemValuePair[];		// テーブルIDとKEYを格納する配列
										// 添字0：テーブルID、添字1：KEY

		String measureMemberKey = "";	// 取得条件に設定するメジャーのKey
		String meaIndex = "";			// メジャーのIndex
		int selectedMeasureNum = 1;	// データ取得対象となるメジャーのメンバー数
	
		for ( i = 0; i < items.length; i++ ) {	// エッジの数だけ実行
			edgeAxisInfoList = (String[])items[i];
			for ( j = 0; j < edgeAxisInfoList.length; j++ ) {	//エッジに配置された軸数分実行
				if ( i == 2 ) {	
				// ページエッジの場合
				// tmpHieItemsの配列の各要素は、テーブルIDとKEYからなる。
				// 書式）<次元ID>:<KEY> 
				// 	例：次元の場合）     1:0
				// 	例：メジャーの場合） 16:<UNAME>(1〜50)
					st = new StringTokenizer(edgeAxisInfoList[j],":");
					tmpItemValuePair = new String[st.countTokens()];
					k = 0;
					while ( st.hasMoreTokens() ) {
						tmpItemValuePair[k] = st.nextToken();
						k++;
					}
					axisID      		= tmpItemValuePair[0];	// 軸ID
					measureMemberKey 	= tmpItemValuePair[1];	// 軸メンバーKEY
					selectedMeasureNum 	= 1;
				} else {		// 行・列エッジの場合
					axisID = edgeAxisInfoList[j];
					selectedMeasureNum = report.getTotalMeasureMemberNumber();	// キューブの持つ全メジャー数
				}
	
				if ( Constants.MeasureID.equals(axisID) ) {	// メジャー
					String measureLists = "";	// メジャーリスト
					for ( k = 0; k < selectedMeasureNum; k++ ) {
						if ( i == 2 ) {
							meaIndex = measureMemberKey.substring(measureMemberKey.indexOf("_") + 1, measureMemberKey.length());
	
							if ( (meaIndex.length() == 2) && (meaIndex.substring(0,1).equals("0") ) ) {
								meaIndex = meaIndex.substring(1,2);
							}
	
							// (ページに配置された場合)メジャーのデフォルトメンバーを登録
							cellDataSQL.setMeasureDefaultMember(meaIndex);
	
						} else {
							meaIndex = Integer.toString(k+1);
						}
						MeasureMember measureMember = (MeasureMember) report.getMeasure().getAxisMemberByUniqueName(meaIndex);
						if ( measureMember.isSelected() ) {	// セレクタではずされていないならば
							if ( measureLists != "" ) {
								measureLists += ",";
							}

							String measureString = null;
							if (formatValue) { // 値をフォーマット付で取得
								measureString = StringUtil.regReplaceAll("%measure%", "MEASURE[" + meaIndex + "]", measureMember.getMeasureMemberType().getFunction_name());
							} else { // 値を単位フォーマットのみで取得
								String unitFunctionID = measureMember.getMeasureMemberType().getUnitFunctionID();
								measureString = StringUtil.regReplaceAll("%measure%", "MEASURE[" + meaIndex + "]", commonSettings.getMeasureMemberTypeByID(unitFunctionID).getFunction_name());
							}
							measureLists += measureString + " as measure_" + meaIndex;

						}
					}

					// SELECT句
					cellDataSQL.addFactKeyColumnList(i, measureLists);

				} else {	// ディメンション

					String factKeyColumn = "";

					// セレクト句
					if ( Integer.valueOf(axisID).intValue() < 10 ) {
						factKeyColumn = "DIM_0" + axisID;
					} else {

						factKeyColumn = "DIM_"  + axisID;
					}
					cellDataSQL.addFactKeyColumnList(i, factKeyColumn);
	
					// WHERE句
					if ( i == 2 ) {// ページエッジ
						ArrayList tmpList = new ArrayList();
						tmpList.add(measureMemberKey);
						cellDataSQL.putWhereClauseMap(i, factKeyColumn, tmpList);

					} else {		// 行、列エッジ
						String[] selectEdgeKeys = (String[])selectKeys[i];		// 選択された軸メンバーリストの集合(エッジ単位)
						String   selectAxisKeys = selectEdgeKeys[j];			// 選択された軸メンバーリスト
						cellDataSQL.putWhereClauseMap(i, factKeyColumn, StringUtil.splitString(selectAxisKeys,","));

					}
				}
			}
		}
		
		return cellDataSQL;
	}

	// ********** メソッド **********

	/**
	 * セルデータを取得するSQL文字列を求める。
	 * @return SQL文字列
	 */
	public String getSQLString() {

		// SELECT句を出力（ディメンション＋メジャー）
		// メジャーの絞込みはここで反映
		String SQL = "SELECT ";

		SQL += StringUtil.joinList(this.getColFactKeyColumnList(), ",");
		SQL += "," + StringUtil.joinList(this.getRowFactKeyColumnList(), ",");
		if (pageFactKeyColumnList.size()>0) {
			SQL += "," + StringUtil.joinList(this.getPageFactKeyColumnList(), ",");
		}

		SQL += " FROM ";
		SQL += getFactTableName();

		// WHERE句を出力（ディメンションのみ）
		SQL += " WHERE ";

		SQL += this.getWhereClause(this.getColWhereClauseMap());
		if (colWhereClauseMap.size()>0) { // COLにディメンションが配置されている
			if (rowWhereClauseMap.size()>0) {	// ROWにディメンションが配置されている
				SQL += " and ";
			}
		}
		SQL += this.getWhereClause(this.getRowWhereClauseMap());

		if (pageWhereClauseMap.size()>0) {	// ページに軸が配置されていない場合を除く
				SQL += " and ";
			SQL += this.getWhereClause(this.getPageWhereClauseMap());
		}
		
		return SQL;
	}

	// ファクトテーブルからディメンションテーブルへマッピングするカラム名称のリスト(カンマ区切りの文字列)をエッジごとに取得する
	// なお、メジャー情報は除外する
	// return)
	//   string[0]:列エッジのカラムリスト(String,カンマ区切り)
	//   string[1]:列エッジのカラムリスト(String,カンマ区切り)
	//   string[2]:ページエッジのカラムリスト(String,カンマ区切り)

	public String[] getDimsFactKeyColumnList() {
		String axesFactKeyColumnList[] = new String[3];
		axesFactKeyColumnList[0] = StringUtil.joinList(StringUtil.exceptElement(colFactKeyColumnList, "MEASURE"), ",");
		axesFactKeyColumnList[1] = StringUtil.joinList(StringUtil.exceptElement(rowFactKeyColumnList, "MEASURE"), ",");
		axesFactKeyColumnList[2] = StringUtil.joinList(StringUtil.exceptElement(pageFactKeyColumnList, "MEASURE"), ",");
		
		return axesFactKeyColumnList;
	}



	// ********** privateメソッド **********

	/**
	 * セルデータを求めるSQLのWHERE節を求める。
	 * @param edgeWhereClauseMap
	 * @return WHERE節
	 */
	private String getWhereClause(LinkedHashMap edgeWhereClauseMap) {
		
		String edgeWhereClause = "";
		
		Iterator iter = edgeWhereClauseMap.keySet().iterator();
		int i = 0;
		while (iter.hasNext()) {
			if(i>0) {
				edgeWhereClause += " and ";
			}
			String factKeyColumn = (String) iter.next();
			edgeWhereClause += factKeyColumn + " in (";

			ArrayList memberKeyList = (ArrayList) edgeWhereClauseMap.get(factKeyColumn);
			edgeWhereClause += StringUtil.joinList(memberKeyList, ",");
			edgeWhereClause += ")";

			i++;
		}
		
		return edgeWhereClause;
	}




	// ********** Setter メソッド **********

	/**
	 * ファクトのキーカラムリストを追加する
	 * @param edgePositionID エッジID(0:列、1:行、2:ページ)
	 * @param factKeyColumn
	 */
	public void addFactKeyColumnList(int edgePositionID, String factKeyColumn) {
		if (edgePositionID==0) {
			this.colFactKeyColumnList.add(factKeyColumn);
		} else if (edgePositionID==1){
			this.rowFactKeyColumnList.add(factKeyColumn);
		} else if (edgePositionID==2){
			this.pageFactKeyColumnList.add(factKeyColumn);
		} else {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * 行エッジへファクトのキーカラムを追加する。
	 * @param factKeyColumn キーカラム
	 */
	public void addColFactKeyColumnList(String factKeyColumn) {
		this.colFactKeyColumnList.add(factKeyColumn);
	}

	/**
	 * 列エッジへファクトのキーカラムを追加する。
	 * @param factKeyColumn キーカラム
	 */
	public void addRowFactKeyColumnList(String factKeyColumn) {
		this.rowFactKeyColumnList.add(factKeyColumn);
	}

	/**
	 * ページエッジへファクトのキーカラムを追加する。
	 * @param factKeyColumn キーカラム
	 */
	public void addPageFactKeyColumnList(String factKeyColumn) {
		this.pageFactKeyColumnList.add(factKeyColumn);
	}

	/**
	 * ファクトテーブル名を設定する。
	 * @param string ファクトテーブル名
	 */
	public void setFactTableName(String string) {
		this.factTableName = string;
	}

	/**
	 * ファクトテーブル名を設定する。
	 * @param edgePositionID エッジID(0:列、1:行、2:ページ)
	 * @param factKeyColumn キーカラム
	 * @param memberKeyList メンバーキーリスト
	 */
	public void putWhereClauseMap(int edgePositionID, String factKeyColumn, ArrayList memberKeyList) {
		if (edgePositionID == 0){
			this.colWhereClauseMap.put(factKeyColumn, memberKeyList);
		} else if (edgePositionID == 1) {
			this.rowWhereClauseMap.put(factKeyColumn, memberKeyList);
		} else if (edgePositionID == 2) {
			this.pageWhereClauseMap.put(factKeyColumn, memberKeyList);
		} else {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * 列のWHERE節をあらわすMapオブジェクトを追加する。
	 * @param factKeyColumn キーカラム
	 * @param memberKeyList メンバーキーリスト
	 */
	public void putColWhereClauseMap(String factKeyColumn, ArrayList memberKeyList) {
		this.colWhereClauseMap.put(factKeyColumn, memberKeyList);
	}


	/**
	 * 行のWHERE節をあらわすMapオブジェクトを追加する。
	 * @param factKeyColumn キーカラム
	 * @param memberKeyList メンバーキーリスト
	 */
	public void putRowWhereClauseMap(String factKeyColumn, ArrayList memberKeyList) {
		this.rowWhereClauseMap.put(factKeyColumn, memberKeyList);
	}

	/**
	 * ページのWHERE節をあらわすMapオブジェクトを追加する。
	 * @param factKeyColumn キーカラム
	 * @param memberKeyList メンバーキーリスト
	 */
	public void putPageWhereClauseMap(String factKeyColumn, ArrayList memberKeyList) {
		this.pageWhereClauseMap.put(factKeyColumn, memberKeyList);
	}

	/**
	 * ページのWHERE節をあらわすMapオブジェクトを追加する。
	 * @param string メジャーのデフォルトメンバーKEY
	 */
	public void setMeasureDefaultMember(String string) {
		measureDefaultMember = string;
	}


	// ********** Getter メソッド **********

	/**
	 * 列エッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリストを求める。
	 * @return キーカラムリスト
	 */
	public ArrayList getColFactKeyColumnList() {
		return colFactKeyColumnList;
	}

	/**
	 * 列に配置された軸のWHERE節をあらわすオブジェクトを求める。
	 * @return 列に配置された軸のWHERE節をあらわすオブジェクト
	 */
	public LinkedHashMap getColWhereClauseMap() {
		return colWhereClauseMap;
	}

	/**
	 * ファクトテーブル名を求める。
	 * @return ファクトテーブル名
	 */
	public String getFactTableName() {
		return factTableName;
	}

	/**
	 * ページエッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリストを求める。
	 * @return キーカラムリスト
	 */
	public ArrayList getPageFactKeyColumnList() {
		return pageFactKeyColumnList;
	}

	/**
	 * ページに配置された軸のWHERE節をあらわすオブジェクトを求める。
	 * @return ページに配置された軸のWHERE節をあらわすオブジェクト
	 */
	public LinkedHashMap getPageWhereClauseMap() {
		return pageWhereClauseMap;
	}

	/**
	 * 行エッジに配置された軸に対応するFactテーブルのカラム、取得対象メジャーリストを求める。
	 * @return キーカラムリスト
	 */
	public ArrayList getRowFactKeyColumnList() {
		return rowFactKeyColumnList;
	}

	/**
	 * 行に配置された軸のWHERE節をあらわすオブジェクトを求める。
	 * @return ページに配置された軸のWHERE節をあらわすオブジェクト
	 */
	public LinkedHashMap getRowWhereClauseMap() {
		return rowWhereClauseMap;
	}

	/**
	 * メジャーがページエッジにあるときのデフォルトメンバーキー(UName)を求める。
	 * @return デフォルトメンバーキー
	 */
	public String getMeasureDefaultMember() {
		return measureDefaultMember;
	}


}
