/*
このファイルの中身は、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) ______________________________________.
*/

//
//	AD同期処理 設定ファイルロードクラス
//			変更履歴
//				2004.01.08	新規作成		二條智文
//
package jp.co.ntt.lms.xmo.Ad;

// JavaAPI
import java.util.*;
import java.io.*;
import java.net.*;

// XMSAPI
import jp.co.ntt.lms.xmo.XMOEnvironment;


/**
 * AD同期処理 設定ファイルロードクラスです。<BR>
 * 設定項目の整合性チェックも行います。
 * @author T.Nijou
 * @version 0.0.0.1 2004/01/08
 */
public class ADCorpEnv  implements java.io.Serializable {


	// 設定ファイルパス
	private static final String mstrFileName = "/conf/xmo/Ad/ADcorp.ini";

	// Singltonとして動作するために自分のインスタンスを保持します
	private static ADCorpEnv objEnv;

	// 整合性チェック時のエラー情報を格納します。
	ADCorpSetupInvalidException objInvalidException = null;

	// AD同期処理設定データを保持します。
	private ADCorpSetupData[] mobjSetupList;

	/**
	 * インスタンスを返却します。<BR>
	 * 初めてのアクセス時にこのクラスはAD設定ファイルを読み込みます。<BR>
	 * 設定ファイルが存在しない場合は、何も行いません。<BR>
	 * 設定ファイルの読み込みに失敗した場合は
	 * ADCorpSetupIOException例外をスローします。
	 * @return GroupsEnv
	 * @exception ADCorpSetupIOException
	 */
	public static ADCorpEnv getInstance()
		throws ADCorpSetupIOException
	{
		if( objEnv == null ) {
			objEnv = new ADCorpEnv();
		}

		return objEnv;
	}

	/**
	 * コンストラクタです。<BR>
	 * AD同期設定ファイルを読み込み、設定データクラスを作成します。<BR>
	 * 設定ファイルが存在しない場合は何も行いません。
	 * 設定ファイルの読み込みに失敗した場合、
	 * ADCorpSetupIOException例外をスローします。
	 * @exception ADCorpSetupIOException
	 */
	private ADCorpEnv()
		throws ADCorpSetupIOException
	{
		// 設定ファイルの読み込み
		URL objURL = this.getClass().getResource( mstrFileName );
		if( objURL == null ) {
			// 設定ファイルが存在しない場合は何もしない。
			return;
		}

		File objFile = new File( objURL.getFile() );

		// FileReaderを開く
		BufferedReader objReader = null;

		try {
			objReader = new BufferedReader(
									new FileReader( objFile ) );

			// 設定ファイルの内容を取得し、設定データリストを生成します。
			getSetup(objReader);
		}
		catch( FileNotFoundException fe ) {
			// ここでFileNotFouncExceptionが発生した場合はエラーとする
			// アクセス権限がない場合などでファイルが読み込めない場合はここに入る
			throw new ADCorpSetupIOException(
						"AD同期処理::設定ファイル読み込みに失敗しました。");
		}
		catch( IOException ie ) {
			// ファイル読み込み失敗時はエラーを返す
			throw new ADCorpSetupIOException(
						"AD同期処理::設定ファイル読み込みに失敗しました。");
		}
		finally {
			// ストリームを閉じる
			if( objReader != null ) {
				try {
					objReader.close();
				}
				catch(IOException e) {
					// ファイルのクローズに失敗した場合はエラーを変えす。
					throw new ADCorpSetupIOException(
						"AD同期処理::設定ファイルのクローズに失敗しました。");
				}
			}
		}
	}

	/**
	 * 設定に不整合がないかを返します。
	 * @return boolean true: 不整合はない false: 不整合有り
	 */
	public boolean isValid() {
		return (objInvalidException == null);
	}

	/**
	 * 不整合の例外を返します。不整合がない場合はnullを返します。
	 * @return ADCorpSetupInvalidException 不整合例外クラス
	 */
	public ADCorpSetupInvalidException getInvalidInfo() {
		return objInvalidException;
	}

	/**
	 * 不整合の設定ドメイン番号を返します。不整合なドメイン設定番号が
	 * ない場合は長さ0の配列を返します。
	 * @return Integer[]
	 */
	public ADCorpSetupData[] getInvalidSetupDataList() {
		// 不整合がない場合は長さ０の配列を返す。
		if( objInvalidException == null ) {
			return new ADCorpSetupData[0];
		}
		// 設定データリストが存在しない場合は長さ０の配列を返す。
		if( mobjSetupList == null ) {
			return new ADCorpSetupData[0];
		}

		Vector vecTemp = new Vector();
		for( int i = 0; i < mobjSetupList.length; i++ ) {
			if( !mobjSetupList[i].isValid() ) {
				vecTemp.add( mobjSetupList[i] );
			}
		}

		ADCorpSetupData[] objResList = new ADCorpSetupData[vecTemp.size()];
		vecTemp.copyInto( objResList );
		return objResList;
	}

	/**
	 * すべてのドメインの設定情報を取得します。<BR>
	 * 設定データはドメイン設定番号の昇順で返されます。
	 * ここで返されるドメイン情報はデータの不整合のないものです。<BR>
	 * 設定ファイルに記述されているドメインの設定で不整合なものは含まれません。<BR>
	 * 設定ファイルに不整合があるかどうかは isValid()関数で確認してください。<BR>
	 * ドメイン設定情報が1つも存在しない場合は、長さ0の配列が返されます。
	 * @return ADCorpSetupData[]
	 */
	public ADCorpSetupData[] getSetupDataList() {
		// 設定データリストが存在しない場合は長さ０の配列を返す。
		if( mobjSetupList == null ) {
			return new ADCorpSetupData[0];
		}

		Vector vecTemp = new Vector();
		for( int i = 0; i < mobjSetupList.length; i++ ) {
			if( mobjSetupList[i].isValid() ) {
				vecTemp.add( mobjSetupList[i] );
			}
		}

		ADCorpSetupData[] objResList = new ADCorpSetupData[vecTemp.size()];
		vecTemp.copyInto( objResList );
		return objResList;
	}

	/**
	 * 指定されたドメイン設定番号の設定データを取得します。
	 * 設定データが存在しない場合はnullを返します。
	 * @param intDomainNo ドメイン設定番号
	 * @return ADCorpSetupData 設定データ
	 */
	public ADCorpSetupData getSetupData(int intDomainNo ) {
		// 設定データリストが存在しない場合はnullを返す。
		if( mobjSetupList == null ) {
			return null;
		}

		ADCorpSetupData objTempData = null;
		for( int i = 0; i < mobjSetupList.length; i++ ) {
			if( intDomainNo == mobjSetupList[i].getDomainNo() ) {
				objTempData = mobjSetupList[i];
				break;
			}
		}

		return objTempData;
	}

	/**
	 * 指定されたドメイン名とroot_DNの最初のドメイン名がマッチした
	 * ドメイン設定データを返します。マッチするドメイン設定が取得出来ない場合は
	 * NULLを返します。
	 * 文字列は大文字小文字を区別せずに比較します。
	 * <PRE>
	 *    例:
	 *       strDomainName = "ntt"
	 *       の場合
	 *       root_DN が
	 *           dc=ntt,dc=co,dc=jp
	 *           dc=NTT,dc=co,dc=jp
	 *       など
	 * </PRE>
	 * @param strDomainName ドメイン名(完全ドメイン名ではない)
	 * @return ADCorpSetupData 設定データ
	 */
	public ADCorpSetupData getDomainSetupData( String strDomainName ) {

		if( strDomainName == null ) {
			return null;
		}

		ADCorpSetupData[] objSetupList = getSetupDataList();

		String strTemp = null;
		for( int i = 0; i < objSetupList.length; i++ ) {
			strTemp = objSetupList[i].getRootDN();

			int intCommaPoint =strTemp.indexOf(",");
			int intEqualPoint = -1;
			if( intCommaPoint >= 0 ) {
				strTemp = strTemp.substring( 0, intCommaPoint );

				intEqualPoint = strTemp.indexOf("=");

				if( intEqualPoint >= 0 ) {
					strTemp = strTemp.substring( intEqualPoint + 1, strTemp.length() );

					if( strDomainName.equalsIgnoreCase( strTemp ) ) {
						return objSetupList[i];
					}
				}
			}
		}

		return null;
	}

//----------------------------------- 内部関数 ---------------------------------
	/**
	 * ファイルから、設定項目を取り出し、設定データリストを生成する。
	 * 以下のデータ整合性のチェックを行いデータ不整合がある場合は、その情報を保持する。
	 * @param in ファイルストリーム
	 * @exception IOException ファイル読み込み例外
	 */
	private void getSetup( BufferedReader in )
		throws IOException
	{
		// 設定項目保持Hashtable
		Hashtable htbSetup = new Hashtable();
		// 設定データ保持Hasthable (キー: ドメイン設定番号)
		Hashtable htbSetupData = new Hashtable();

		// 1行分のデータを保持
		String strLine = null;
		// 行数を保持
		int intLineCount = 0;
		while( (strLine = in.readLine()) != null ) {
			// 行を１プラス
			intLineCount++;
			strLine.trim();

			// コメント行、空白行の確認
			if( strLine.equals("") || strLine.charAt(0) == '#' ) {
				// コメント行、空白行なら読み飛ばす。
				continue;
			}

			// キー = 値 の整合性チェック
			if( strLine.indexOf( '=' ) <=0 ) {
				// 不正な設定なのでエラーとする。
				String strMessage = "不正な記述形式です。\n行番号:: " + intLineCount + "\n値:: " + strLine;
				objInvalidException = new ADCorpSetupInvalidException( strMessage );
				// 処理を中断する
				return;
			}

			// 文字列を キーと値に分割してHashtableに格納します。
			String strKey = strLine.substring(0, strLine.indexOf( '=' ) ).trim();
			String strValue = strLine.substring(strLine.indexOf( '=' ) + 1).trim();
			int intDomainNo = getDomainNo( strKey );

			// 有効なキーかどうかチェックします。
			if( !checkKey(strKey) ) {
				addInvalidInfo( "line- " + intLineCount + " - 無効なキーです。:: " + strKey );

				// コンテニュー
				continue;
			}

			// キーから取得したドメイン設定番号が不正の場合
			if( intDomainNo < 0 ) {
				addInvalidInfo( "line- " + intLineCount + " - ドメイン設定番号が不正です。:: " + strKey );

				// コンテニュー
				continue;
			}


			// 設定項目の重複チェック
			if( htbSetup.containsKey(strKey) ) {
				// すでに登録されている値の場合はエラーとする。
				addInvalidInfo( "line- " + intLineCount + " - " + strKey + "の設定が重複しています。" );
				// データクラスの不整合フラグをonにする。
				ADCorpSetupData objSetupData = (ADCorpSetupData)htbSetupData.get( new Integer(intDomainNo) );
				objSetupData.setInvalid();
				// コンテニュー
				continue;
			}

			// 設定項目データクラスを作成
			// Hashからすでに登録されている設定データを取得する。
			ADCorpSetupData objSetupData = (ADCorpSetupData)htbSetupData.get( new Integer(intDomainNo) );
			if( objSetupData == null ) {
				// なかったら作ってハッシュに入れる。
				objSetupData = new ADCorpSetupData(intDomainNo);
				htbSetupData.put(new Integer(intDomainNo), objSetupData);
			}

			// 設定項目を保存する。
			htbSetup.put( strKey, strValue );

			// 値を設定データに格納する。
			setData( objSetupData, strKey, strValue );

		}

		// ハッシュ内のデータをドメイン設定番号の昇順の配列に変換します。
		mobjSetupList = changeArray( htbSetupData );

	}

	/**
	 * 不整合情報の追加
	 * @param info 不整合情報
	 */
	private void addInvalidInfo( String info ) {
		if( objInvalidException == null ) {
			objInvalidException = new ADCorpSetupInvalidException();
		}
		objInvalidException.addInvalidInfo( info );
	}

	/**
	 * 有効なキーか判断します。
	 * @param strKey チェックするキー
	 * @return boolean true: 有効 false: 無効
	 */
	private boolean checkKey( String strKey ) {

		if( strKey == null ) {
			return false;
		}

		boolean check = false;

		// キーリストと照会する。
		for( int i = 0; i < ADCorpSetupData.KEY_LIST.length; i++ ) {
			if( strKey.indexOf( ADCorpSetupData.KEY_LIST[i] ) == 0 ) {
				check = true;
				break;
			}
		}

		return check;

	}
	/**
	 * キーらかドメイン設定番号を取得します。
	 * ドメイン設定番号が不正の場合は-1を返します。
	 * @param strKey キー
	 * @return int ドメイン設定番号 不正の場合は-1
	 */
	private int getDomainNo( String strKey ) {
		if( strKey == null || strKey.length() < 3 ) {
			return -1;
		}

		String strNo = strKey.substring( strKey.length() - 2, strKey.length() );

		int intRes = -1;

		try {
			intRes = Integer.parseInt(strNo);
		}
		catch(NumberFormatException ne ) {
			return -1;
		}

		if( intRes < 1 || intRes > 99 ) {
			return -1;
		}

		return intRes;
	}

	/**
	 * キーに対応するフィールドに値を格納します。<BR>
	 * 対応するフィールドがない場合は何もしない。
	 * @param objData 設定データ
	 * @param strKey キー
	 * @param strValue 値
	 */
	private void setData( ADCorpSetupData objData, String strKey, String strValue ) {
		if( objData == null || strKey == null ) {
			return;
		}

		if( strKey.indexOf(ADCorpSetupData.ROOTDN) == 0 ) {
			// 検索ルート
			objData.setRootDN( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.ADHOST) == 0 ) {
			// ADホスト名
			objData.setADHost( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.ADPORT) == 0 ) {
			// ADポート番号
			objData.setADPort( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.ADUSER) == 0 ) {
			// AD接続ユーザ
			objData.setADUser( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.ADPASS) == 0 ) {
			// AD接続パスワード
			objData.setADPass( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.ADFILTER) == 0 ) {
			// 検索フィルタ
			objData.setADFilter( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.OBJKEY) == 0 ) {
			// オブジェクト判別キー
			objData.setObjKey( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.DN) == 0 ) {
			// DN属性
			objData.setDN( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.REFKEY) == 0 ) {
			// 所属参照属性
			objData.setRefKey( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.USEROBJKEY) == 0 ) {
			// ユーザオブジェクト判別キー
			objData.setUserObjKey( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.BINARY_ATTRIBUTE) == 0 ) {
			// バイナリ属性指定
			objData.setBinaryAttribute( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_PASSWORD) == 0 ) {
			// P パスワード
			objData.setPPassword( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_LOGINID) == 0 ) {
			// P ログインID
			objData.setPLoginID( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_FOREIGNID) == 0 ) {
			// P 外部ID
			objData.setPForeignID( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_NAME) == 0 ) {
			// P 名前
			objData.setPName( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_SEX) == 0 ) {
			// P 性別
			objData.setPSex( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_READINGNAME) == 0 ) {
			// P 読みかた
			objData.setPReadingName( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_BIRTHDAY) == 0 ) {
			// P 誕生日
			objData.setPBirthday( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_CONTACTTEL) == 0 ) {
			// P 電話番号
			objData.setPContactTel( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_CONTACTADDRESS) == 0 ) {
			// P 住所
			objData.setPContactAddress( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_CONTACTEMAIL) == 0 ) {
			// P メールアドレス１
			objData.setPContactEmail( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_CONTACTEMAIL2) == 0 ) {
			// P メールアドレス２
			objData.setPContactEmail2( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.P_CUSTUMMSGDIR) == 0 ) {
			// P コメント
			objData.setPCustumMsgDir( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.G_FOREIGNID) == 0 ) {
			// G 外部ID
			objData.setGForeignID( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.G_NAME) == 0 ) {
			// G 名前
			objData.setGName( strValue );
		}
		else if( strKey.indexOf(ADCorpSetupData.G_CUSTUMMSGDIR) == 0 ) {
			// G コメント
			objData.setGCustumMsgDir( strValue );
		}
	}

	/**
	 * ハッシュ内のデータのうち整合の取れたデータをドメイン設定番号の
	 * 昇順の配列に変換します。
	 * @param Hashtable htbSetupData
	 * @return ADCorpSetupData[] ドメイン設定番号の昇順
	 */
	private ADCorpSetupData[] changeArray( Hashtable htbSetupData ) {

		if( htbSetupData == null ) {
			return null;
		}

		// ハッシュからキーを取得する。
		Vector vecKeyList = new Vector();

		Enumeration enum = htbSetupData.keys();
		while(enum.hasMoreElements() ) {
			vecKeyList.add(enum.nextElement());
		}

		// ソート
		Collections.sort(vecKeyList);

		Vector vecDataList = new Vector();
		for(int i = 0; i < vecKeyList.size(); i++ ) {
			ADCorpSetupData objData = (ADCorpSetupData)htbSetupData.get(vecKeyList.get(i));
			// データのチェックを行う。
			if( !objData.isValid() ) {
				// 不整合があった場合はその情報を不整合情報に格納する
				addInvalidInfo( objData.getInvalidInfo() );
			}
			vecDataList.add(objData);
		}

		// 配列に変換する。
		ADCorpSetupData[] objList = new ADCorpSetupData[vecDataList.size()];
		vecDataList.copyInto(objList);

		return objList;
	}



}
