/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	フレームワーク　パラメータオブジェクト
//			変更履歴
//				2004/01/08 新規作成 二條
//
package jp.co.ntt.lms.xms.framework;

import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jp.co.ntt.lms.xmo.util.DebugLog;
import jp.co.ntt.lms.xmo.util.EditUploadFile;
import jp.co.ntt.lms.xmo.util.UploadParam;
import jp.co.ntt.lms.xms.common.util.StringUtil;

/**
 * フレームワーク　パラメータオブジェクト<BR>
 * 各Httpリクエストのビジネスロジッククラスに渡されるオブジェクト
 * @author nijou
 */
public class XmsRequest {
	/**
	 * リクエスト・パラメータのデフォルトエンコード。
	 */
	public static final String REQEUST_DEFAULT_ENCODING = "ISO-8859-1";

	/**
	 * HTTPリクエストを保持する。
	 */
	private HttpServletRequest request = null;

	/**
	 * HTTPレスポンスを保持する。
	 */
	private HttpServletResponse response = null;

	/**
	 * パラメータ・マップ。<br>
	 * パラメータの名称をキー、「対応するパラメータ値(String)を要素とするList」を値とするMap。
	 */
	private Map parameterMap;

	/**
	 * ファイル・パラメータ・マップ。<br>
	 * パラメータの名称をキー、
	 * 「対応するパラメータの入力項目&lt;input type=&quot;file&quot;&gt;に
	 * 入力したファイルパス(String)を要素とするList」を値とするMap。
	 */
	private Map filePathMap;

	/**
	 * リクエストのENC-TYPEがmultipart/form-dataの場合、true。
	 */
	private boolean multiPartEncType;

	/**
	 * HTTPリクエストとHTTPレスポンスを設定する。
	 * @param request HTTPリクエスト
	 * @param response HTTPレスポンス
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	public XmsRequest(HttpServletRequest request, HttpServletResponse response)
		throws XmsException {
		this.request = request;
		this.response = response;

		// パラメータ・マップ。
		parameterMap = makeNewMap();
		// ファイル・パラメータ・マップ。
		filePathMap = makeNewMap();
		// multipart/form-data判定
		// ContentTypeを取得して、その先頭に特有の文字列があるか判定する。
		String contentType = request.getContentType();
		DebugLog.write(
			this.getClass(),
			"request.getContentType()" + contentType,
			DebugLog.ROW);
		if (contentType == null ) {
			contentType = "";
		}
		setMultiPartEncType(
			contentType.startsWith(Constant.CONTENT_TYPE_PREFIX_MULTIPART));

		// multipart/form-dataによる送信の場合
		if (isMultiPartEncType() == true) {
			analyzeMultiPartRequest(request);
		}
		// 通常の送信の場合
		else {
			analyzeNormalRequest(request);
		}

		////////////////////////////////////////////////////////////////////////
		// ログ出力
		//======================================================================
		// アクションID
		DebugLog.write(
			this.getClass(),
			Constant.REQUEST_ACTION_ID
				+ ":"
				+ getParameter(Constant.REQUEST_ACTION_ID),
			DebugLog.HIGHT);
		//======================================================================
		// パラメータ全体
		Set keySet = null;
		keySet = this.getParmaterNames();
		for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
			String parameterName = null;
			String parameterValue = null;
			String parameterFile = null;
			List parameterList = null;
			List fileList = null;

			parameterName = (String) iterator.next();
			parameterList = getParameters(parameterName);
			fileList = getParameterFilePathes(parameterName);
			for (int i = 0; i < parameterList.size(); i++) {
				String paramLog = null;
				parameterValue = (String) parameterList.get(i);
				if (i < fileList.size()) {
					parameterFile = (String) parameterList.get(i);
				}
				paramLog =
					"["  + parameterName + "]"
					+ "[" + parameterValue + "]"
					+ "[" + parameterFile + "]";
				DebugLog.write(this.getClass(), paramLog, DebugLog.MID);
			}

		}

	}

	/**
	 * HTTPリクエストを取得する。
	 * @return HttpServletRequest
	 */
	public HttpServletRequest getRequest() {
		return request;
	}

	/**
	 * HTTPレスポンスを取得する。
	 * @return HttpServletResponse
	 */
	public HttpServletResponse getResponse() {
		return response;
	}

	/**
	 * リクエスト・パラメータ名称を戻す。<br>
	 * リクエスト・パラメータ名称(String)を要素とするSetを戻す。
	 * パラメータが存在しない場合、要素数０のSetを戻す。
	 * このメソッドが戻したSetに修正を加えても、
	 * 本クラスの内部データには影響しない。
	 * @return リクエスト・パラメータ名称(String)を要素とするSet。
	 */
	public Set getParmaterNames() {
		// このメソッドの戻り値。
		Set parameterNames = null;
		// パラメータマップのkeySetメソッド戻り値
		Set parameterMapKeySet = null;

		parameterMapKeySet = parameterMap.keySet();
		parameterNames = new HashSet(parameterMapKeySet);
		return parameterNames;
	}

	/**
	 * 指定した名称に対応するパラメータ値を戻す。<br>
	 * 対応するパラメータが存在しない場合、nullを戻す。
	 * 指定したパラメータ名称に対応するパラメータが複数存在する場合、
	 * getParameters(String)の戻り値の先頭要素を戻す。
	 *
	 * @param name 取得するリクエスト・パラメータ名称。
	 * @return 指定した名称に対応するパラメータ値。
	 * @throws XmsException 処理中に例外が発生した場合。
	 * @see #getParameters(String)
	 */
	public String getParameter(String name) throws XmsException {
		// このメソッドの戻り値
		String returnValue = null;
		// getParametersメソッドによって得られたメソッド。
		List parameterList = null;

		try {
			// パラメータリストを取得
			parameterList = getParameters(name);
			// 空の場合、nullを戻す。
			// 要素がある場合、先頭を戻す。
			if (parameterList.isEmpty()) {
				returnValue = null;
			}
			else {
				returnValue = (String) parameterList.get(0);
			}
		}
		catch (XmsException e) {
			throw e;
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		return returnValue;
	}

	/**
	 * 指定した名称に対応するパラメータ値を戻す。<br>
	 * 指定したパラメータ名称に対応するパラメータの値（String）を
	 * 要素とするListを戻す。
	 * 対応するパラメータが存在しない場合、要素数０のListを戻す。
	 * このメソッドが戻したListに修正を加えても、
	 * 本クラスの内部データには影響しない。
	 *
	 * @param name 取得するリクエスト・パラメータ名称。
	 * @return 指定した名称に対応するパラメータ値を要素とするList。
	 * @throws XmsException 処理中に例外が発生した場合。
	 * @see #getParameter(String)
	 */
	public List getParameters(String name) throws XmsException {
		// このメソッドの戻り値
		List returnList = null;
		// パラメータマップから取得したリスト
		List parameterList = null;

		try {
			// 戻り値の初期化
			returnList = makeNewList();
			// リストから取得（生成しない）
			parameterList = getParameterList(parameterMap, name, false);
			// リストを取得した場合、戻り値にすべて移動
			if (parameterList != null) {
				returnList.addAll(parameterList);
			}
		}
		catch (XmsException e) {
			throw e;
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		return returnList;
	}

	/**
	 * 指定したパラメータに入力したファイルパスを戻す。<br>
	 * 指定したパラメータに対応するパラメータがファイルアップロードに利用する
	 * &lt;input type=&quot;file&quot;&gt;の場合、入力したファイルパスを戻す。
	 * 対応するパラメータが存在しない、または、指定したパラメータが
	 * &lt;input type=&quot;file&quot;&gt;ではない場合、nullを戻します。
	 * 指定したパラメータ名称に対応するパラメータが複数存在する場合、
	 * getParameterFilePathes(String)の戻り値の先頭要素を戻す。
	 *
	 * @param name 取得するリクエスト・パラメータ名称。
	 * @return 指定した名称に対応する入力項目に指定したファイルパス。
	 * @throws XmsException 処理中に例外が発生した場合。
	 * @see #getParameterFilePathes(String)
	 */
	public String getParameterFilePath(String name) throws XmsException {
		// このメソッドの戻り値
		String returnValue = null;
		// getParameterFilePathesメソッドによって得られたメソッド。
		List filePathList = null;

		try {
			// ファイル・パラメータリストを取得
			filePathList = getParameterFilePathes(name);
			// 空の場合、nullを戻す。
			// 要素がある場合、先頭を戻す。
			if (filePathList.isEmpty()) {
				returnValue = null;
			}
			else {
				returnValue = (String) filePathList.get(0);
			}
		}
		catch (XmsException e) {
			throw e;
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		return returnValue;
	}

	/**
	 * 指定したパラメータに入力したファイルパスを戻す。<br>
	 * 指定したパラメータに対応するパラメータがファイルアップロードに利用する
	 * &lt;input type=&quot;file&quot;&gt;の場合、入力したファイルパス(String)を
	 * 要素とするListを戻す。
	 * 対応するパラメータが存在しない、または、指定したパラメータが
	 * &lt;input type=&quot;file&quot;&gt;ではない場合、
	 * 要素数０のListを戻す。
	 * このメソッドが戻したListに修正を加えても、
	 * 本クラスの内部データには影響しない。
	 *
	 * @param name 取得するリクエスト・パラメータ名称。
	 * @return 指定した名称に対応する入力項目に指定した
	 *          ファイルパスを要素とするList。
	 * @throws XmsException 処理中に例外が発生した場合。
	 * @see #getParameterFilePath(String)
	 */
	public List getParameterFilePathes(String name) throws XmsException {
		// このメソッドの戻り値
		List returnList = null;
		// ファイル・パラメータマップから取得したリスト
		List filePathList = null;

		try {
			// 戻り値の初期化
			returnList = makeNewList();
			// リストから取得（生成しない）
			filePathList = getParameterList(filePathMap, name, false);
			// リストを取得した場合、戻り値にすべて移動
			if (filePathList != null) {
				returnList.addAll(filePathList);
			}
		}
		catch (XmsException e) {
			throw e;
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		return returnList;
	}

	/**
	 * 送信されたリクエストのENC-TYPEを判定する。<br>
	 * リクエストのenctypeがmultipart/form-dataを指定した場合、trueを戻す。
	 * それ以外の場合、falseを戻す。
	 *
	 * @return 現在のリクエストのENC-TYPEがmultipart/form-dataの場合true。
	 */
	public boolean isMultiPartEncType() {
		return multiPartEncType;
	}

	/**
	 * 送信されたリクエストのENC-TYPE判定用に設定する。<br>
	 * @param multiPartEncType リクエストのenctypeがmultipart/form-dataを
	 *                          指定した場合、trueを渡す。
	 */
	private void setMultiPartEncType(boolean multiPartEncType) {
		this.multiPartEncType = multiPartEncType;
	}

	/**
	 * パラメータマップから指定した名称のパラメータに対応するパラメータを戻す。<br>
	 * 指定した名称に対応するパラメータリストがマップにない場合、createの値を参照する。
	 * createがtrueの場合、新規にリストを生成し、指定した名称でパラメータマップに登録して、
	 * そのリストを戻す。
	 * createがfalseの場合、生成せず、nullを戻す。
	 * @param parameterMap パラメータマップ。
	 * @param name パラメータ名称。
	 * @param create 説明を参照。
	 * @return パラメータマップから指定した名称のパラメータに対応するパラメータリスト。
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	private static List getParameterList(
		Map parameterMap,
		String name,
		boolean create)
		throws XmsException {
		List parameterList = null;
		try {
			// パラメータリストを取得
			parameterList = (List) parameterMap.get(name);
			// リストがなく、作成の指示がある場合
			if (parameterList == null && create == true) {
				parameterList = makeNewList();
				parameterMap.put(name, parameterList);
			}
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		return parameterList;
	}

	/**
	 * 新規にリストを生成して戻す。<br>
	 * @return 新規に生成したList。
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	private static List makeNewList() throws XmsException {
		return new ArrayList();
	}

	/**
	 * 新規にマップを生成して戻す。<br>
	 * @return 新規に生成したMap。
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	private static Map makeNewMap() throws XmsException {
		return new HashMap();
	}

	/**
	 * 通常のリクエストパラメータ解析を実施する。<br>
	 * @param request HTTPリクエストオブジェクト。
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	private void analyzeNormalRequest(HttpServletRequest request)
		throws XmsException {

		////////////////////////////////////////////////////////////////////////
		// エンコード決定
		String actionId = request.getParameter(Constant.REQUEST_ACTION_ID);
		String encoding = XmsBusinessXMLPerser.getActionEncoding(actionId);
		////////////////////////////////////////////////////////////////////////
		// パラメータ名を取得
		Enumeration nameEnumeration = request.getParameterNames();
		while (nameEnumeration.hasMoreElements() == true) {
			// パラメータ名
			String parameterName = (String) nameEnumeration.nextElement();
			// パラメータ値を取得
			String[] parameterValues =
					request.getParameterValues(parameterName);
			// マップ上に登録されいるパラメータリストを取得。
			// ない場合は作成・登録。
			// パラメータリスト
			List parameterList = getParameterList(
					parameterMap, parameterName, true);
			// パラメータごとに値をリストに登録。
			for (int i = 0; i < parameterValues.length; i++) {
				// パラメータ値
				String parameterValue = parameterValues[i];
				// エンコードを取得できている場合で、かつ、
				// デフォルトと異なる場合のみエンコード変換する
				String encodedValue = null;
				if ((StringUtil.isNullString(encoding) == false)
					&& (encoding.equals(REQEUST_DEFAULT_ENCODING) == false)) {
					try {
						encodedValue = new String(
							parameterValue.getBytes(
								REQEUST_DEFAULT_ENCODING),
							encoding);
					}
					catch (UnsupportedEncodingException e) {
						encodedValue = parameterValue;
					}
				}
				else {
					encodedValue = parameterValue;
				}
				// パラメータリストに追加
				parameterList.add(encodedValue);
			}
		}
	}

	/**
	 * Multipart/form-dataのリクエストパラメータ解析を実施する。<br>
	 *
	 * @param request HTTPリクエストオブジェクト。
	 * @throws XmsException 処理中に例外が発生した場合。
	 */
	private void analyzeMultiPartRequest(HttpServletRequest request)
		throws XmsException {
		// ファイルアップロード・パースクラス
		EditUploadFile parser = null;
		//ファイルアップロード・パース結果クラス
		UploadParam param = null;
		//ファイルアップロード・パース結果
		Vector parseResult = null;
		// コンテントタイプ
		String contentType = null;
		// リクエストデータ読み込み用
		BufferedReader reader = null;
		// パラメータ名
		String parameterName = null;
		// パラメータ値
		String parameterValue = null;
		// ファイルパラメータ値
		String fileName = null;

		// パラメータリスト
		List parameterList = null;
		// ファイルパラメータリスト
		List fileParameterList = null;
		// 例外発生フラグ(trueが発生)
		boolean exceptionFlag = false;

		try {
			// 例外発生フラグ、仮に発生したものとする。
			exceptionFlag = true;

			// リーダー、コンテントタイプを取得し、パース開始
			contentType = request.getContentType();
			reader = request.getReader();
			parser = new EditUploadFile();
			parseResult = parser.getUploadParamList(reader, contentType);

			for (int i = 0; i < parseResult.size(); i++) {
				// パース後のパラメータを取得
				param = (UploadParam) parseResult.get(i);

				// パラメータ名称、値、ファイル名称を取得
				parameterName = param.getFormName();
				parameterValue = param.getMainData();
				fileName = param.getFileName();

				// パラメータ名称をキーに値を登録
				parameterList =
					getParameterList(parameterMap, parameterName, true);
				parameterList.add(parameterValue);

				// パラメータ名称をキーにファイル名称を登録
				fileParameterList =
					getParameterList(filePathMap, parameterName, true);
				fileParameterList.add(fileName);
			}

			exceptionFlag = false;
		}
		catch (XmsException e) {
			throw e;
		}
		catch (Exception e) {
			throw new XmsInnerException(e);
		}
		finally {
			// リーダのクローズ。
			// 例外はtryブロック内で例外が発生しなかった場合のみスロー。
			if (reader != null) {
				try {
					reader.close();
				}
				catch (Exception e) {
					if (exceptionFlag == false) {
						throw new XmsSystemException(e);
					}
				}
				finally {
					reader = null;
				}
			}
		}
	}
}
