/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.mail;

import static org.opengion.fukurou.util.StringUtil.csv2ArrayOnly;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import org.opengion.fukurou.db.DBUtil;
import org.opengion.hayabusa.common.HybsSystem;

/**
 * メール定型文及びそれも基づいて各項目の合成を行うクラスです。
 * コンストラクタには定型文ID及びシステムIDで定型文マスタよりメールの定型文を取得します。
 * メール各項目のゲッターでは、定型文の内容を元にパラメータ値とマージして各項目を合成します。
 * 宛先について、セットした社員ID、グループIDと定型文の宛先設定に基づき、社員マスタと
 * グループマスタよりメールアドレス情報を取得して宛先マップを作成します。
 *
 * @og.group メールモジュール
 *
 * @version  4.0
 * @author   Sen.Li
 * @since    JDK1.6
 */
public class MailPattern {

	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
	private static final String CONTENTS = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "CONTENT" : "CONTENTS";
	private static final String ADDRESS = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "MEMBER" : "ADDRESS";
	private static final String NAME_JA = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "NAME" : "NAME_JA";
	private static final String KBNAME = HybsSystem.sysBool( "VER4_COMPATIBLE_MODE" ) ? "NM_KBN" : "KBNAME";

	// 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS
	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
//	private static final String selGE31 	= "SELECT PTN_ID,FROM_ID,TO_ID,CC_ID,BCC_ID,TITLE,CONTENTS"
	private static final String selGE31 	= "SELECT PTN_ID,FROM_ID,TO_ID,CC_ID,BCC_ID,TITLE,"+CONTENTS
											+ " FROM GE31"
											+ " WHERE SYSTEM_ID =? AND PTN_ID=? AND FGJ='1'";
	// 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS , NM_KBN ⇒ KBNAME , NAME ⇒ NAME_JA
	// 5.2.0.0 (2010/09/01) Ver4互換モード対応
//	private static final String	selGE33		= "SELECT A.ADDRESS,A.NAME_JA,B.NAME_JA"
	private static final String	selGE33		= "SELECT A."+ADDRESS+",A."+NAME_JA+",B."+NAME_JA
											+ " FROM GE33 A,GE33 B"
											+ " WHERE A.FGJ='1' AND B.FGJ(+)='1' AND A.GROUP_ID=B.GROUP_ID(+)"
//											+ " AND A.KBNAME='1' AND B.KBNAME(+)='0' AND A.SYSTEM_ID=B.SYSTEM_ID(+)"
											+ " AND A."+KBNAME+"='1' AND B."+KBNAME+"(+)='0' AND A.SYSTEM_ID=B.SYSTEM_ID(+)"
											+ " AND A.SYSTEM_ID =? "
											+ " AND A.GROUP_ID =?";
	// 5.3.5.0 (2011/05/01) Ver4対応漏れ対応
//	private static final String	selGE35		= "SELECT  NAME_JA,MAIL"
	private static final String	selGE35		= "SELECT  "+NAME_JA+",MAIL"
											+ " FROM GE35"
											+ " WHERE USERID=?";
	// 内部データのカラム番号(定型文マスタテーブル)
	// 5.1.9.0 (2010/09/01) public ⇒ private へ変更
//	private static final int GE31_PTN_ID	= 0 ;		// 未使用
	private static final int GE31_FROM_ID	= 1 ;
	private static final int GE31_TO_ID		= 2 ;
	private static final int GE31_CC_ID		= 3 ;
	private static final int GE31_BCC_ID	= 4 ;
	private static final int GE31_TITLE		= 5 ;
	private static final int GE31_CONTENTS	= 6 ;		// 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS

	// 内部データのカラム番号(グループマスタ)
	private static final int GE33_ADDRESS 	= 0 ;		// 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS
	private static final int GE33_MNAME	 	= 1 ;
	private static final int GE33_GNAME	 	= 2 ;
	// 内部データのカラム番号(社員マスタ)
	private static final int GE35_NAME	 	= 0 ;
	private static final int GE35_MAIL 		= 1 ;

	// 内部データのカラム番号(宛先テーブル)
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_DST_ID   	= 0 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_GROUP_ID 	= 1 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_GROUP_NAME	= 2 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_DST_NAME 	= 3 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_DST_ADDR 	= 4 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_DST_KBN  	= 5 ;
	/** カラム番号(宛先テーブル) {@value} */
	public static final int IDX_FGJ 	  	= 6 ;

	/** メール送信区分 {@value} */
	public static final int KBN_TO			= 0 ;	// メール送信区分(TO)
	/** メール送信区分 {@value} */
	public static final int KBN_CC			= 1 ;	// メール送信区分(CC)
	/** メール送信区分 {@value} */
	public static final int KBN_BCC			= 2 ;	// メール送信区分(BCC)

	private static final String PreFixGroup = "GP.";
	private final List<String> errAddrList 		= new ArrayList<String>();
//	private Map<String, String> paramMap 	= new HashMap<String, String>();
	private final Map<String, String> paramMap ;
	private String  fromId		= null;
	private final String 	toId	;
	private final String 	ccId	;
	private final String 	bccId	;
	private String[][] 	title	= null;
	private String[][] 	content = null;
	private Map<String, String[]> mailDstMap;

	private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );		// 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対応

	/**
	 * メール定型文オブジェクトを作成するコンストラクタです。
	 * 定型文マスタより取得したデータを各フィルードにセットします。
	 *
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param	params	パラメータのマップ
	 */
	public MailPattern( final Map<String, String> params ){
		paramMap = params;
		String sysId = params.get( "SYSTEM_ID" );
		String pid = params.get( "PTN_ID" );
		String[] selGE30Args = { sysId,pid };
//		String[][] ptn = DBUtil.dbExecute( selGE31, selGE30Args, AbstractMailManager.appInfo );
		String[][] ptn = DBUtil.dbExecute( selGE31, selGE30Args, AbstractMailManager.appInfo, DBID );		// 5.5.5.1 (2012/08/07)
		if( ptn == null || ptn.length <=0 ) {
			String errMsg = "定型文取得できません。システムID：" + sysId + "定型文ID：" + pid ;
			throw new RuntimeException( errMsg );
		}
		fromId 	= ptn[0][GE31_FROM_ID];
		toId 	= ptn[0][GE31_TO_ID];
		ccId 	= ptn[0][GE31_CC_ID];
		bccId 	= ptn[0][GE31_BCC_ID];
		String tit = ptn[0][GE31_TITLE];
		if( tit != null && tit.length() > 0 ) {
			title = splitParam( tit );
		}
		String con = ptn[0][GE31_CONTENTS];			// 5.1.0.0 (2009/11/04) CONTENT ⇒ CONTENTS
		if( con != null && con.length() > 0 ) {
			content = splitParam( con );
		}
	}

	/**
	 * 定型文の送信者ID欄の設定値とパラメータ{&#064;FROM}を元に送信者アドレスを取得します。
	 * 定型文には{&#064;FROM}とセットされている場合は、パラメータ｛&#064;FROM｝の値を元に、社員IDをセット
	 * されている場合、直接そのIDを元に社員マスタから送信者アドレスを取得します。
	 * 送信者アドレス取得できなければ例外を投げます。
	 *
	 * @og.rev 4.3.7.5 (2009/07/08) 送信元名称が設定されていない場合は、アドレスを<>で囲わない
	 *
	 * @return	送信者アドレス
	 */
	public String getFromAddr(){
		// 定型文に{&#064;FROM}⇒ユーザーよりセットしたデータで置換える、ユーザーIDの場合はそのまま
		if( "{@FROM}".equals( fromId ) ) {
			fromId = paramMap.get( "FROM" );
		}
		paramMap.put( "FROM_ID"	, fromId );	// 送信者ID

		String[] userInf = getUserInfo( fromId );
		String fromAddr = null;
		if( userInf != null && checkAddr ( fromId, userInf[1] ) ){ 	// 送信者メールアドレスチェック
			paramMap.put( "FROM_NAME", userInf[0] );				// 送信者名前
			// 4.3.7.5 (2009/07/08)
			if( userInf[0] != null && userInf[0].length() > 0 ) {
				fromAddr = userInf[0] + "<" + userInf[1] + ">" ;
			}
			else {
				fromAddr = userInf[1];
			}
			paramMap.put( "FROM_ADDR", fromAddr );					// 送信者メールアドレス
		}
		else {
			String errMsg = "送信者ユーザー情報エラー。ユーザーID：" + fromId;
			throw new RuntimeException( errMsg );
		}

		return fromAddr;
	}

	/**
	 * マージ済のメールタイトルを返します。
	 *
	 * @return	メールタイトル
	 *
	 */
	public String getTitle(){
		return marge( title );
	}

	/**
	 * マージ済のメール本文を返します。
	 *
	 * @og.rev 5.1.0.0 (2009/11/04) HEADER ⇒ H_TXT , FOOTER ⇒ F_TXT カラム名変更
	 *
	 * @return	メール本文
	 */
	public String getContent(){
		String header = paramMap.get( "HEADER" );		// 5.1.0.0 (2009/11/04) HEADER ⇒ H_TXT
		StringBuilder contentBuf = new StringBuilder();
		if ( header != null ) {
			contentBuf.append( header ).append( '\n' );
		}
		contentBuf.append( marge( content ) ).append( '\n' );
		String fooder = paramMap.get( "FOOTER" );		// 5.1.0.0 (2009/11/04) FOOTER ⇒ F_TXT
		if ( fooder != null ) {
			contentBuf.append( fooder );
		}

		return contentBuf.toString();
	}

	/**
	 * 引数の文字列により、定数文字列の部分とパラメータの部分を分解します。
	 * 例："A{&#064;PARAM1}B"⇒rtn[0][0]="A",rtn[0][1]="B",rtn[1][0]="PARAM1"
	 *
	 * @param	src		分解対象の文字列
	 *
	 * @return	定数文字列の部分とパラメータの部分を分解した配列
	 */
	private String[][] splitParam( final String src ) {
		String[][] rtn = new String[2][];
		if( src == null ) { return new String[2][0]; }

		ArrayList<String> listCons = new ArrayList<String>() ;
		ArrayList<String> listPara = new ArrayList<String>() ;

		int start = 0;
		int index = src.indexOf( "{@" );
		while( index >= 0 ) {
			listCons.add( src.substring( start, index ) );
			int end = src.indexOf( '}',index );
			if( end < 0 ) {
				String errMsg = "{@ と } との対応関係がずれています。"
							+ "src=[" + src + "] : index=" + index ;
				throw new RuntimeException( errMsg );
			}
			listPara.add( src.substring( index + 2, end ));

			start = end+1 ;
			index = src.indexOf( "{@",start );
		}
		listCons.add ( src.substring( start ) );

		rtn[0] = listCons.toArray( new String[listCons.size()] );
		rtn[1] = listPara.toArray( new String[listPara.size()] );

		return rtn;
	}

	/**
	 * 送信先のアドレスをセットします。
	 * 定型文に定義されている宛先(TO、CC、BCC)を引数として渡します。引数には{&#064;TO}、{&#064;CC}、{&#064;BCC}が含まれています。
	 * {&#064;TO}、{&#064;CC}、{&#064;BCC}を初期設定の値で置換えて、実のメールアドレスマップを作成します。
	 *
	 * @return 宛先のマップ
	 */
	public Map<String, String[]> getDstMap(){
		String[] toBuf = getDstArray( toId );
		String[] ccBuf = getDstArray( ccId );
		String[] bccBuf = getDstArray( bccId );

		// 送信先(TO、CC、BCC)のマップを作成します。
		mailDstMap =  (TreeMap<String, String[]>)getDstAddrMap( bccBuf, KBN_BCC );
		mailDstMap.putAll( (TreeMap<String, String[]>) getDstAddrMap( ccBuf, KBN_CC ) );
		mailDstMap.putAll( (TreeMap<String, String[]>) getDstAddrMap( toBuf, KBN_TO ) );
		setDstWord();

		return mailDstMap;
	}

	/**
	 * アドレスエラーのメッセージリストを返します。
	 *
	 * @return	メッセージリスト
	 */
	public List<String> getErrList(){
		return errAddrList;
	}

	/**
	 * 送信先のアドレスをcsv形式から配列にセットします。
	 * 引数は定型文マスタにセットしたものです。例：{&#064;TO},C12345,GP.IT
	 * ｛&#064;TO｝、｛&#064;CC｝、｛&#064;BCC｝はパラメータテーブルにセットしたもので置き換えます。
	 * 分解後の配列には、ユーザーID及びグループIDが混在します。
	 *
	 * @param	csvId	csv形式のアドレス
	 *
	 * @return	送信先のアドレスの配列
	 */
	private String[] getDstArray( final String csvId ){
		String[] csvArr = csv2ArrayOnly( csvId );
		ArrayList<String> list = new ArrayList<String>();

		String[] tmp;
		int size = csvArr.length;
		for( int i = 0; i < size; i++ ){
			if( csvArr[i].startsWith( "{@" )){
				tmp = csv2ArrayOnly( paramMap.get( csvArr[i].substring( 2, csvArr[i].length() - 1 ) ) );
				int len = tmp.length;
				for(int j=0;j<len;j++){
					list.add( tmp[j] );
				}
			}
			else{
				list.add( csvArr[i]);
			}
		}
		return list.toArray( new String[list.size()] );
	}

	/**
	 * 送信先のアドレス・マップを作成します。
	 *　引数 dstBuf にはユーザーIDとグループID混在する配列です。
	 *　ユーザーIDの場合、社員マスタのビューから名前及びメールアドレスを取得してマップにセットします。
	 *　グループIDの場合、グループマスタより、名前及びメールアドレスを取得してマップにセットします。
	 *　重複のユーザーが存在する場合、最後にセットした方が採用されます。
	 *
	 * @og.rev 5.1.0.0 (2009/11/04) MEMBER ⇒ ADDRESS カラム名変更
	 *
	 * @param	dstBuf	ユーザーIDとグループID混在する配列
	 * @param	kbn		送信区分[0:TO/1:CC/2:BCC]
	 *
	 * @return	送信先のアドレス・マップ
	 */
	private Map<String, String[]> getDstAddrMap( final String[] dstBuf, final int kbn ){
		Map<String,String[]> dstMap= new TreeMap<String,String[]>();
		 // IDX_DST_ID ,IDX_GROUP_ID, IDX_GROUP_NAME ,IDX_DST_NAME ,IDX_DST_ADDR ,IDX_DST_KBN ,IDX_FGJ
		 String[] dstInit = { "", "", "", "", "", Integer.toString( kbn ), "7" };

		 int len = dstBuf.length;
		 for( int i=0; i < len; i++ ){
			 if( dstBuf[i].startsWith( PreFixGroup ) ) { // グループIDの場合、グループマスタより、メンバーを取得します。
				String[][] groupUsers = getGroupUsers( dstBuf[i].substring( PreFixGroup.length() ) );
				if( groupUsers.length > 0 ) {
					int memberCnt = groupUsers.length;
					for( int j = 0; j < memberCnt; j++ ) { // グループメンバーの処理
						String[] grpMember = dstInit.clone();
						grpMember[IDX_GROUP_ID]   = dstBuf[i].substring( PreFixGroup.length() ); // グループID
						grpMember[IDX_GROUP_NAME] = groupUsers[j][GE33_GNAME];    	// グループ名
						grpMember[IDX_DST_ID]     = groupUsers[j][GE33_ADDRESS];   	// 宛先ID
						grpMember[IDX_DST_NAME]   = groupUsers[j][GE33_MNAME];    	// 宛先名
						if( groupUsers[j][GE33_ADDRESS].contains( "@" ) ) {       	// メールアドレスがセットされる場合
							grpMember[IDX_DST_ADDR] = groupUsers[j][GE33_ADDRESS]; 	//　メールアドレス
						}
						else { // 社員IDがセットされた場合
							String[] userAddr = getUserInfo( groupUsers[j][GE33_ADDRESS] );
							if ( userAddr != null && userAddr.length > 0 ){
								grpMember[IDX_DST_ADDR] = userAddr[GE35_MAIL]; 	 	//　メールアドレス
							}
						}
						if ( checkAddr( grpMember[IDX_DST_ID], grpMember[IDX_DST_ADDR] ) ){ // アドレス構文チェック
							grpMember[IDX_FGJ] = AbstractMailManager.FGJ_SEND_WAIT;      // 送信待
						}
						else {
							grpMember[IDX_FGJ] = AbstractMailManager.FGJ_ADDR_ERR;       // アドレス取得エラー
							errAddrList.add( "アドレス取得エラー。ユーザーID：" + grpMember[IDX_DST_ID] + " アドレス：" + grpMember[IDX_DST_ADDR] );
						}
						dstMap.put( groupUsers[j][GE33_ADDRESS], grpMember );
					}
				}
				else { // グループマスタよりメンバー取得できない場合
					String[] emptyGp = dstInit.clone();
					emptyGp[IDX_GROUP_ID] = dstBuf[i]; // グループID
					emptyGp[IDX_GROUP_NAME] = "*"; // グループID
					emptyGp[IDX_DST_ID] = "NO-MEMBER"; // 宛先ID
					dstMap.put( dstBuf[i], emptyGp );
				}
			}
			else { // ユーザーIDの場合
				String[] indMember = dstInit.clone();
				indMember[IDX_DST_ID] = dstBuf[i]; 							// 宛先ID
				indMember[IDX_GROUP_ID] = "*"; 								// グループID
				indMember[IDX_GROUP_NAME] = "*"; 							// グループID
				String[] userAddr = getUserInfo( dstBuf[i] );
				if ( userAddr != null && userAddr.length > 0 ){
					indMember[IDX_DST_NAME] = userAddr[GE35_NAME]; 			// 宛先名
					indMember[IDX_DST_ADDR] = userAddr[GE35_MAIL]; 			//　メールアドレス
					if ( checkAddr( indMember[IDX_DST_ID], userAddr[1] ) ) {
						indMember[IDX_FGJ] = AbstractMailManager.FGJ_SEND_WAIT;
					}
					else {
						indMember[IDX_FGJ] = AbstractMailManager.FGJ_ADDR_ERR;
						errAddrList.add( "アドレス取得エラー。ユーザーID：" + indMember[IDX_DST_ID] + " アドレス：" + indMember[IDX_DST_ADDR] );
					}

				}
				else {
					indMember[IDX_FGJ] = AbstractMailManager.FGJ_ADDR_ERR; // 状況コード
				}
				dstMap.put( dstBuf[i], indMember );
			}
		}
		 return dstMap;
	}

	/**
	 * グループマスタより、ユーザー情報を取得します。
	 * 戻り値の配列には、ユーザーIDまたはメールアドレス、ユーザー名、グループ名が格納されています。
	 *
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param	groupId	グループID
	 *
	 * @return	ユーザー情報
	 */
	private String[][] getGroupUsers( final String groupId ){
		String sysId = paramMap.get( "SYSTEM_ID" );
		String[] ge33SelArgs = { sysId,groupId };
//		String[][] ge33Datas = DBUtil.dbExecute( selGE33,ge33SelArgs,AbstractMailManager.appInfo );
		String[][] ge33Datas = DBUtil.dbExecute( selGE33,ge33SelArgs,AbstractMailManager.appInfo, DBID );		// 5.5.5.1 (2012/08/07)

		if ( ge33Datas.length == 0 ) {
			String errMsg = "グループ情報取得できません。グループID：" + groupId ;
			if( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ){
				throw new RuntimeException( errMsg );
			}
			else {
				errAddrList.add( errMsg );
			}
		}
		return ge33Datas;
	}

	/**
	 * アドレスの構文チェックを行います、合法の場合は'true'(取得できた)、違法の場合は'false'(有効アドレス取得できず)を返します。
	 *
	 * @param   userId  ユーザーID
	 * @param   address アドレス
	 *
	 * @return  アドレスの構文チェック結果(true:取得/false:取得できず)
	 */
	private boolean checkAddr( final String userId, final String address ) {
		boolean rtn = true;
		try {
 			new InternetAddress( address );
		}
		catch ( AddressException aep ) {
			if ( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ) {
				String errMsg = "ユーザーメールアドレスエラー。ユーザーID：" + userId + " アドレス：" + address;
				throw new RuntimeException( errMsg,aep );
			}
			rtn = false;
		}
		return rtn;
	}

	/**
	 * 引数の配列から文書を合成します。
	 * src[0]に配列には定数文字列、src[1]にはパラメータ
	 *
	 * @param	src	引数の配列
	 *
	 * @return	合成された文章
	 */
	private String marge( final String[][] src ){
		StringBuilder rtnBuf = new StringBuilder();
		String rtn;
		if ( src != null ){
			int len = src[1].length;
			for(int i=0; i< len; i++) {
				rtnBuf.append( src[0][i] );
				rtnBuf.append( paramMap.get( src[1][i] ) );
			}
			rtnBuf.append( src[0][len] );
			rtn = rtnBuf.toString();
		}
		else {
			rtn = null;
		}

		return rtn;
	}

	/**
	 * 宛先(TO、CC、BCC)のID、名前、メールアドレスをパラメータマップにセットします。
	 *
	 */
	private void setDstWord() {
		StringBuilder to_id    = new StringBuilder();
		StringBuilder to_name  = new StringBuilder();
		StringBuilder to_addr  = new StringBuilder();
		StringBuilder cc_id    = new StringBuilder();
		StringBuilder cc_name  = new StringBuilder();
		StringBuilder cc_addr  = new StringBuilder();
		StringBuilder bcc_id   = new StringBuilder();
		StringBuilder bcc_name = new StringBuilder();
		StringBuilder bcc_addr = new StringBuilder();
		int kbn;
		for( String dstId : mailDstMap.keySet() ) {
			kbn = Integer.parseInt( mailDstMap.get( dstId )[IDX_DST_KBN]);
			switch( kbn ) {
				case KBN_TO:
					to_id.append( ',' ).append( dstId );
					to_name.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					to_addr.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					to_addr.append( '<' ).append( mailDstMap.get( dstId )[IDX_DST_ADDR] ).append( ">" );
					break;
				case KBN_CC:
					cc_id.append( ',' ).append( dstId );
					cc_name.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					cc_addr.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					cc_addr.append( '<' ).append( mailDstMap.get( dstId )[IDX_DST_ADDR] ).append( ">" );
					break;
				case KBN_BCC:
					bcc_id.append( ',' ).append( dstId );
					bcc_name.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					bcc_addr.append( ',' ).append( mailDstMap.get( dstId )[IDX_DST_NAME] );
					bcc_addr.append( '<' ).append( mailDstMap.get( dstId )[IDX_DST_ADDR] ).append( ">" );
					break;
				default:
					String errMsg = "このアドレス区分がサポートされません。区分：" + kbn;
					throw new RuntimeException( errMsg );
			}
		}
		// 予約語マップに追加します。
		paramMap.put( "TO_ID"   , ( to_id.length()    > 0 ) ? to_id.toString().substring( 1 ):"" );
		paramMap.put( "TO_NAME" , ( to_name.length()  > 0 ) ? to_name.toString().substring( 1 ):"" );
		paramMap.put( "TO_ADDR" , ( to_addr.length()  > 0 ) ? to_addr.toString().substring( 1 ):"" );
		paramMap.put( "CC_ID"   , ( cc_id.length()    > 0 ) ? cc_id.toString().substring( 1 ):"" );
		paramMap.put( "CC_NAME" , ( cc_name.length()  > 0 ) ? cc_name.toString().substring( 1 ):"" );
		paramMap.put( "CC_ADDR" , ( cc_addr.length()  > 0 ) ? cc_addr.toString().substring( 1 ):"" );
		paramMap.put( "BCC_ID"  , ( bcc_id.length()   > 0 ) ? bcc_id.toString().substring( 1 ):"" );
		paramMap.put( "BCC_NAME", ( bcc_name.length() > 0 ) ? bcc_name.toString().substring( 1 ):"" );
		paramMap.put( "BCC_ADDR", ( bcc_addr.length() > 0 ) ? bcc_addr.toString().substring( 1 ):"" );
	}

	/**
	 * 社員マスタより名前、メールアドレスを取得します。
	 * 戻り値 rtn[0]:ユーザー名、 rtn[1]:ユーザーメールアドレス
	 *
	 * @og.rev 4.3.6.6 (2009/05/15) メールアドレスが直接指定された場合に対応
	 * @og.rev 5.5.5.1 (2012/08/07) リソース系DBID 付け忘れ対策
	 *
	 * @param   userId  ユーザーID
	 *
	 * @return	配列文字列(rtn[0]:ユーザー名、 rtn[1]:ユーザーメールアドレス)
	 */
	private String[] getUserInfo( final String userId ){
		String[] rtn = null;

		if ( userId.contains( "@" ) ) {
			rtn = new String[2];
			rtn[0] = "";
			rtn[1] = userId;
		}
		else {
			String[] ge35SelArgs = { userId };
//			String[][] ge35Datas = DBUtil.dbExecute( selGE35,ge35SelArgs,AbstractMailManager.appInfo );
			String[][] ge35Datas = DBUtil.dbExecute( selGE35,ge35SelArgs,AbstractMailManager.appInfo, DBID );		// 5.5.5.1 (2012/08/07)
			if ( ge35Datas.length > 0) {
				rtn = ge35Datas[0];
			}
			else {
				String errMsg = "ユーザー情報取得できません。ユーザーID：" + userId ;
				if( "true".equals( paramMap.get( "ADDR_CHECK" ) ) ){
					throw new RuntimeException( errMsg );
				}
				else {
					errAddrList.add( errMsg );
				}
			}
		}
		return rtn;
	}
}
