/*
 * 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.taglib;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.resource.GUIInfo;
import org.opengion.hayabusa.resource.UserInfo;
import org.opengion.hayabusa.resource.CodeData;
import org.opengion.hayabusa.resource.FavoriteGUIData;
import org.opengion.hayabusa.resource.ResourceManager;
import org.opengion.fukurou.util.XHTMLTag;
import org.opengion.fukurou.util.FileMap;
import org.opengion.fukurou.util.TagBuffer;
import org.opengion.fukurou.util.ToString;						// 6.1.1.0 (2015/01/17)
import org.opengion.fukurou.util.StringUtil ;

import static org.opengion.fukurou.util.StringUtil.nval ;
import static org.opengion.fukurou.util.HybsConst.BUFFER_LARGE;		// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.util.HybsConst.BR;				// 6.1.0.0 (2014/12/26) refactoring

import java.util.Set ;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Map;

import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

/**
 * 画面アクセスメニューを作成します。
 *
 * 画面リソースの階層番号(レベル)は、
 * 　0:グループ分類メニュー(class="GUI_GRP"のtdタグで囲われます)
 * 　1:トップ階層(【分類名称】)
 * 　2:選択階層(通常の折りたたみメニュー)
 * 　3:選択非表示(通常は、隠してあります)
 * です。
 * お気に入りメニューを登録するキーワードに、HybsSystem.GUI_FAV_MENU_KEY を使用しています。
 * これは、エンジン内部で使用しているキーワードです。
 *
 * @og.formSample
 * ●形式：&lt;og:topMenu /&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:topMenu
 *       menuType           【TAG】作成するメニューの種類(NORMAL,GROUP,ONELEVEL,NEXTGUI,MATRIX,MATRIX2)を指定します(初期値:NORMAL)
 *       expand             【TAG】折り返しメニューを構築するかどうかを指定します(初期値:true)
 *       groups             【TAG】表示対象となるグループをCSV形式で指定します
 *       classify           【TAG】表示対象となる分類(classify)を指定します
 *       href               【TAG】グループメニューの表示対象となるソース名(href)を指定します(初期値:menu.jsp)
 *       target             【TAG】グループメニューの表示対象となるフレーム名(target)を指定します(初期値:MENU)
 *       imageOnly          【TAG】グループメニューの表示に、画像のみかどうか[true:画像のみ/false:画像＋ラベル]を指定します(初期値:false)
 *       sideCount          【TAG】グループメニューの表示対象となるメニューを横並びさせる数を指定します
 *       minCellCount       【TAG】表形式メニュー(MATRIX)の一つのセルに含まれる最小行数を指定します(初期値:8)
 *       maxCellCount       【TAG】表形式メニュー(MATRIX)の一つのセルに含まれる最大行数を指定します(初期値:8)
 *       cache              【TAG】グループメニューのキャッシュを使用するかどうか[true/false]を指定します(初期値:true)
 *       match              【TAG】正判定(マッチする場合に、メニューに出す)条件を設定します
 *       unmatch            【TAG】逆判定(マッチする場合に、メニューに出さない)条件を設定します
 *       useButton          【TAG】画面リンクにボタンを使用するかどうか[true/false]を指定します(初期値:false)
 *       buttonRequest      【TAG】マトリクスからの遷移先でボタンメニュー表示するかどうか[true/false]を指定します(初期値:false)
 *       inlineStyle        【TAG】標準画面を初期状態で表示するかどうか[true/false]を指定します(初期値:false)
 *       useDivOneLevel     【TAG】ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうか[true/false]を指定します(初期値:false)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *       useButtonScript    【廃止】ボタンリンクを使用した場合に、JavaScriptのイベントを発生させるかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:topMenu&gt;
 *
 * ●使用例
 *    &lt;og:topMenu /&gt;
 *
 *    &lt;og:topMenu
 *          menuType     = "NORMAL"      NORMAL:通常 / GROUP:グループメニュー / ONELEVEL:１レベル / NEXTGUI:次アクセス先
 *                                           / MATRIX:一覧表メニュー / MATRIX2:一覧表メニュー(大分類なし版)
 *          expand       = "true"        true:折り返しメニュー / false:階層メニュー
 *          groups       = "AA,BB,CC"    表示対象となるグループをCSV形式で指定します。
 *          classify     = "ABC"         表示対象となる分類を指定します。
 *          href         = "menu.jsp"    グループメニューの対象ソース名(href)を指定します。
 *          target       = "MENU"        グループメニューの対象フレーム名(target)を指定します。
 *          imageOnly    = "false"       グループメニューに、 true:画像のみ / false:画像＋ラベル を指定します。
 *          sideCount    = "6"           グループメニューの表示対象となるメニューを横並びさせる数を指定します。
 *          minCellCount = "8"           表形式メニューの１セルの最小行数を指定します。
 *          maxCellCount = "8"           表形式メニューの１セルの最大行数を指定します。
 *          cache        = "true"        グループメニューのキャッシュを使用するかどうか指定します。
 *          match        = "正規表現"    正判定(マッチする場合に、メニューに出す)条件を設定します。
 *          unmatch      = "正規表現"    逆判定(マッチする場合に、メニューに出さない)条件を設定します。
 *          useButton    = "false"       ボタン形式のリンクを使用するかを指定します。
 *          useDivOneLevel  = "false"    ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうかを指定します。
 *    /&gt;
 *
 * @og.rev 3.5.5.3 (2004/04/09) 新規作成
 * @og.group メニュー制御
 *
 * @version  4.0
 * @author   Kohei Naruse
 * @since    JDK5.0,
 */
public class TopMenuTag extends CommonTagSupport {
	//* このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.2.2.0 (2015/03/27)" ;

	private static final long serialVersionUID = 622020150327L ;

	private static final String FIELD_IN  = "<fieldset style=\"display:inline;\">";
	private static final String FIELD_OUT = "</fieldset>" + BR;
	private static final String JSP = HybsSystem.sys( "JSP" );

	private static final String CNTX	 = HybsSystem.sys( "CONTEXT_NAME" );	// 5.5.4.2 (2012/07/13) META-INF/resources 対応
	private static final String MENU_IMG = "/jsp/menuImage/" ;					// 5.5.4.2 (2012/07/13) META-INF/resources 対応

	private boolean multiSessionCheck = HybsSystem.sysBool( "USE_MULTI_SESSION_CHECK" );

	private static final String[] MENU_TYPE = new String[] { "NORMAL","GROUP","ONELEVEL","NEXTGUI","MATRIX","MATRIX2" } ;	// 5.2.3.0 (2010/12/01)
	private String   menuType	= MENU_TYPE[0] ;	// NORMAL:通常 / GROUP:グループメニュー / ONELEVEL:１レベル / MATRIX:一覧表メニュー / MATRIX2:一覧表メニュー(大分類なし版)
	private boolean  expand		= true;				// true:折り返しメニュー / false:階層メニュー
	private String[] groups		;					// 表示対象となるグループをCSV形式で指定します。
	private String   selClassify;					// 表示対象となる分類を指定します。
	private String   href		= "menu.jsp";		// グループメニューの対象ソース名(href)を指定します。
	private String   target		= "MENU";			// グループメニューの対象フレーム名(target)を指定します。
	private boolean  imageOnly	;					// グループメニューに、 true:画像のみ / false:画像＋ラベル を指定します
	private int      sideCount	= -1;				// 5.2.3.0 (2010/12/01) 表示対象となるメニューを横並びさせる数を指定します。
	private int      minCellCount	= 8;			// 表形式メニューの１セルの最小行数を指定します。
	private int      maxCellCount	= 8;			// 表形式メニューの１セルの最大行数を指定します。
	private boolean  cache		= true;				// グループメニューのキャッシュを使用するかどうか指定します。
	// 3.8.8.7 (2007/05/01) メニューの表示条件指定追加
	private String match		;					// 正判定(マッチする場合に、メニューに出す)条件を設定
	private String unmatch		;					// 逆判定(マッチする場合に、メニューに出さない)条件を設定

	private transient FileMap imgFileMap	;		// 5.5.2.5 (2012/05/21) 属性名変更

	// MULTI_SESSION_CHECK 時のリンクに付加する情報
	private String	mscKey		;

	private boolean  useButton		;				// ボタン形式のリンクを使用するか 4.2.1.0 (2008/04/01)
	private boolean  buttonRequest	;				// マトリクス２からの遷移でボタン形式にするかのリクエスト変数 4.2.1.0 (2008/04/17)
	private boolean  excludeButton	;				// 4.3.3.0 (2008/10/01) ボタンメニューの場合でも強制的に従来のプルダウンにします。
	private boolean  inlineStyle	;				// 4.3.3.0 (2008/10/01) trueでLv3の画面にstyle属性でinlineを付加

	private String		helpLinkFormat	;				// 5.3.9.0 (2011/09/01) ヘルプリンクフォーマット
	private transient	FileMap helpMap	;				// 5.3.9.0 (2011/09/01) ヘルプファイルが存在するかどうかのキャッシュ

	private boolean		useDivOneLevel	;			// 5.5.2.3 (2012/05/15) ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうかを指定します。

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 4.0.0.0 (2007/10/31) 1レベルメニューの見直し
	 * @og.rev 4.2.1.0 (2008/04/01) マトリクスメニュー2追加
	 * @og.rev 5.2.3.0 (2010/12/01) NEXTGUI 追加
	 * @og.rev 5.3.0.0 (2010/11/22) NEXTGUI もマルチセッションチェックをしない。
	 * @og.rev 5.3.9.0 (2011/09/01) メニューでのヘルプアイコン対応
	 * @og.rev 5.5.0.4 (2012/03/12) FAQ表示対応
	 * @og.rev 5.5.2.5 (2012/05/21) FAQ対応 廃止
	 * @og.rev 5.5.4.2 (2012/07/13) META-INF/resources からの読み取り対応
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)

		// Ver 4.0.0 2007/09/04
		// ONELEVEL, NEXTGUI の場合は、multiSessionCheck を行いません。
		if( multiSessionCheck && !"ONELEVEL".equals( menuType ) && !"NEXTGUI".equals( menuType ) ) {
			final String cnt = (String)getSessionAttribute( HybsSystem.MULTI_SESSION_CHECK );
			if( cnt == null ) {
				final String errMsg = "マルチセッションチェックのキーが存在しません。"
							+ "topMenu タグを実行する前に、必ず jspInit タグを"
							+ "実行しておいてください。" ;
				jspPrint( errMsg );
				return SKIP_PAGE ;		// ページの残りの処理を行わない。
			}
			mscKey = HybsSystem.MULTI_SESSION_CHECK + "=" + cnt ;
		}

		final Set<?> set = pageContext.getServletContext().getResourcePaths( MENU_IMG );
		imgFileMap = new FileMap( MENU_IMG,set );

		// メニューでのヘルプアイコン対応
		helpLinkFormat = get( "helpLinkFormat" );
		if( helpLinkFormat != null && helpLinkFormat.length() > 0 ) {
			helpMap = new FileMap( get( "helpBaseDir" ) );
		}

		if( "NORMAL".equals( menuType ) ) {
			jspPrint( "<div class=\"forpos\" id=\"dummy\"></div>" + CR );
			if( ! useButton ){ // 4.2.1.0 (2008/04/26) ボタン時にはお気に入りを出さない
				jspPrint( makeFavoriteMenu() );
			}
			jspPrint( makeMenu() );
		}
		else if( "GROUP".equals( menuType ) ) {
			jspPrint( makeGroupMenu() );
		}
	//	else if( "CLASSIFY".equals( menuType ) ) {
	//		jspPrint( makeClassifyMenu() );
	//	}
		else if( "ONELEVEL".equals( menuType ) && selClassify != null ) {
			jspPrint( makeOneLevelMenu() );
		}
	 	// 5.2.3.0 (2010/12/01) NEXTGUI 追加
		else if( "NEXTGUI".equals( menuType ) ) {
			jspPrint( makeNextguiMenu() );
		}
		else if( "MATRIX".equals( menuType ) ) {
			jspPrint( makeMatrixMenu() );
		}
		else if( "MATRIX2".equals( menuType ) ) { // 4.2.1.0 (2008/04/01) 大分類なし版追加
			jspPrint( makeMatrixMenu2() );
		}
	//	else {
	//		jspPrint( "menuType が想定外です。menuType=[" + menuType + "]" );
	//	}

		return EVAL_PAGE ;		// ページの残りを評価する。
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 5.2.3.0 (2010/12/01) sideCountの初期値を -1(無制限)に変更
	 * @og.rev 5.3.9.0 (2011/09/01) メニューでのヘルプアイコン対応
	 * @og.rev 5.5.2.3 (2012/05/15) ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうかを指定します。
	 */
	@Override
	protected void release2() {
		super.release2();
		expand				= true;
		menuType			= MENU_TYPE[0];
		groups				= null;
		selClassify			= null;
		multiSessionCheck	= HybsSystem.sysBool( "USE_MULTI_SESSION_CHECK" );
		href				= "menu.jsp";		// グループメニューの対象ソース名(href)を指定します。
		imageOnly			= false;			// グループメニューに、 true:画像のみ / false:画像＋ラベル を指定します
		target				= "MENU";			// グループメニューの対象フレーム名(target)を指定します。
		sideCount			= -1;				// 5.2.3.0 (2010/12/01) 表示対象となるメニューを横並びさせる数を指定します。
		minCellCount		= 8;				// 表形式メニューの１セルの最小行数を指定します。
		maxCellCount		= 8;				// 表形式メニューの１セルの最大行数を指定します。
		mscKey				= null;				// MULTI_SESSION_CHECK 時のリンクに付加する情報
		imgFileMap			= null;				// 5.5.2.5 (2012/05/21) 属性名変更
		cache				= true;				// グループメニューのキャッシュを使用するかどうか指定します。
		match				= null;				// 正判定(マッチする場合に、メニューに出す)条件を設定
		unmatch				= null;				// 逆判定(マッチする場合に、メニューに出さない)条件を設定
		useButton			= false;			// ボタン形式のリンクを使用するか
		buttonRequest		= false;			// trueでボタンメニューの表示を行う
		excludeButton		= false;			// trueでボタンメニュー時でも強制的に従来のプルダウンにする。
		inlineStyle			= false;			// trueの場合は標準画面(Lv3)にdisplay:inlineを付加
		helpLinkFormat		= null;				// ヘルプリンクフォーマット
		helpMap				= null;				// ヘルプファイルが存在するかどうかのキャッシュ
		useDivOneLevel		= false;			// 5.5.2.3 (2012/05/15) ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうかを指定します。
	}

	/**
	 * メニューを表示する為のHTMLを作成します(折り返しJavaScript対応版)。
	 *
	 * @og.rev 3.5.6.5 (2004/08/09) GUIInfo の comments 属性を title にセットする。
	 * @og.rev 3.6.0.9 (2004/12/03) リアルアドレス設定時に、{&#064;XXXX}処理を追加
	 * @og.rev 3.8.0.0 (2005/06/07) 同一セッションでのマルチ起動対策を行います。
	 * @og.rev 4.0.0.0 (2005/01/31) GUIInfoの実アドレスのパラメータを考慮する。
	 * @og.rev 4.0.0.0 (2007/10/31) 分類の廃止に伴い、全面見直し
	 * @og.rev 4.2.1.0 (2008/04/01) 小分類指定での表示対応(↑の対応での再実装漏れ)
	 * @og.rev 4.2.1.0 (2008/04/11) 小分類をexpandしない場合にはspanタグで囲う。
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 *
	 * @return  メニュー
	 * @og.rtnNotNull
	 */
	private String makeMenu() {
		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );

		final UserInfo userInfo = getUser();

		// 4.0.0 (2005/01/31)

		final GUIInfo[] guiInfos = userInfo.getGUIInfos();
		String gamenId		;			// 画面ID
		int menuNo = 11;
		int kmokuNo = 0;

		boolean isInFieldset = false;			// 大分類の中か？
		boolean isInClassify = false;			// 小分類の中か？
//		boolean isExistHiddenMenu = false;		// 隠しメニューが存在するか？
//		boolean isExistClassifyHidden = false;	// 分類隠しが存在するか？
		boolean isHiddenMenu	= false;		// 隠しメニューが存在するか？	6.0.2.5 (2014/10/31) refactoring
		boolean isClassHidden	= false;		// 分類隠しが存在するか？		6.0.2.5 (2014/10/31) refactoring
		int level ;
		int preLevel = 0;

		for( int i=0; i<guiInfos.length; i++ ) {
			if( guiInfos[i].isRead() ) {		// 4.0.0 (2005/01/31)
				gamenId = guiInfos[i].getKey() ;
				if( match   != null && !gamenId.matches( match  ) ) { continue; }
				if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }

				// 4.2.1.0 (2008/04/01)
				if( selClassify != null ) {
					if( !selClassify.equals( guiInfos[i].getKey() ) && !selClassify.equals( guiInfos[i].getClassify() ) ) {
						continue; // 分類(classify) に含まれない
					}
				}

				// 処理すべき画面かのチェック
				final int guiFlg = guiCheck( guiInfos, i );
				if( guiFlg == 0 ) { continue; }

				level = guiInfos[i].getLevel();		// 4.0.0 (2005/01/31)

				// 隠しメニュー展開用
				if( preLevel >= 3 && level < 3 ) {
//					if( isExistHiddenMenu ) {
					if( isHiddenMenu ) {			// 6.0.2.5 (2014/10/31) refactoring
						rtn.append( makeEllipses( menuNo,kmokuNo,3 ) );
						kmokuNo++;
					}
//					isExistHiddenMenu = false;
					isHiddenMenu = false;			// 6.0.2.5 (2014/10/31) refactoring
				}

				// 大分類(フィールドメニュー)
				if( level == 1 ) {
					if( isInFieldset ) {
						rtn.append( FIELD_OUT );
						rtn.append( "</div>" );
					}
					isInFieldset = true;

					// 隠しメニュー用
					if( expand ) {
						rtn.append( "<div class=\"expand1 " );

						if( guiFlg == 1 ) {
							rtn.append( "unpopular ");
						}

						rtn.append( "\">" );
					}

					rtn.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),level ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応

					isInClassify = false;
				}
				// 小分類・直リンク
				else if( level == 2 ) {
					menuNo++;
					kmokuNo = 0;
					if( expand ) {
						rtn.append( "<div class=\"expand1 " );

						// 隠しメニュー用
						if( guiFlg == 1 ) {
							rtn.append( "unpopular " );
//							isExistClassifyHidden = true;
							isClassHidden = true;			// 6.0.2.5 (2014/10/31) refactoring
						}

						// rtn.append( "\" id=\"menu" + menuNo + "\">" );
						// 4.3.3.0 (2008/10/01) useButton=true時は表示しない
						rtn.append( "\" id=\"menu" + menuNo + "\" ");
						if( useButton ){
							rtn.append( "style=\"display:none\"");
						}
						rtn.append( '>');		// 6.0.2.5 (2014/10/31) char を append する。

					}
					else{	// 4.2.1.0 (2008/04/11)expandしない場合にはspan要素で囲ってnon-expandクラスを指定。
						rtn.append( "<span class=\"non-expand\">" );
					}

					rtn.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),level ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
					rtn.append( BR );
					if( expand ) {
						rtn.append( "</div>" );
					}
					else{	// 4.2.1.0 (2008/04/11)
						rtn.append( "</span>");
					}

					isInClassify = true;
				}
				// 通常メニュー・隠しメニュー
				else if( level >= 3 ) {
					if( isInClassify ) {
						if( expand ) {
							rtn.append( "<div class=\"expand2 " );

							// 隠しメニュー用
							if( level == 4 ) {
								rtn.append( "unpopular " );
//								isExistHiddenMenu = true;
								isHiddenMenu = true;			// 6.0.2.5 (2014/10/31) refactoring
							}

							// rtn.append( "\" id=\"menu" + menuNo + "_" + kmokuNo + "\">" );
							// 4.3.3.0 (2008/10/01) メニューを初期状態で開けるようにする
							// 6.0.2.5 (2014/10/31) char を append する。
//							rtn.append( "\" id=\"menu" + menuNo + "_" + kmokuNo + "\" " );
							rtn.append( "\" id=\"menu" ).append( menuNo ).append( '_' ).append( kmokuNo ).append( "\" " );
							if( inlineStyle && level == 3 ) {
								rtn.append( " style=\"display:inline\"" );
							}
							rtn.append( '>' );		// 6.0.2.5 (2014/10/31) char を append する。
						}

						// 画面IDが"HYBS_BR"の時は処理しない
						if( !"HYBS_BR".equals( guiInfos[i].getKey() ) ) {
							rtn.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),level ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
						}
						rtn.append( BR );
						if( expand ) {
							rtn.append( "</div>" );
						}
						kmokuNo++;
					}
				}

				rtn.append( CR );

				preLevel = level;
			}
		}

		// 終了処理
//		if( isExistHiddenMenu ) {
		if( isHiddenMenu ) {										// 6.0.2.5 (2014/10/31) refactoring
			rtn.append( makeEllipses( menuNo,kmokuNo,3 ) );
		}

		if( isInFieldset ) {
			rtn.append( FIELD_OUT );
			rtn.append( "</div>" );
		}

//		if( isExistClassifyHidden ) {
		if( isClassHidden ) {										// 6.0.2.5 (2014/10/31) refactoring
			rtn.append( makeEllipses( menuNo,0,2 ) );
		}

		return rtn.toString();
	}

	/**
	 * メニューを表示する為のHTMLを作成します(折り返しJavaScript対応版)。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 個人別のお気に入りメニューを作成します。
	 * @og.rev 4.0.0.0 (2007/10/31) 分類項目の廃止
	 * @og.rev 4.1.1.0 (2008/02/05) お気に入り情報はGEA09から取得するように変更
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 *
	 * @return  個人別のお気に入りメニュー
	 * @og.rtnNotNull
	 */
	private String makeFavoriteMenu() {

		final UserInfo userInfo = getUser();
		final Map<String,FavoriteGUIData> favoriteMap = userInfo.getFavoriteMap();

		if( favoriteMap.isEmpty() ) { return ""; }

		final ResourceManager resource = getResource();	// リソース参照
		final String	largeClassify	 = resource.getLabelData( "FAVORITE_MENU" ).getShortLabel(); // お気に入り
		String	lastClassify	 = "";				// 前方画面の分類
		int		menuNo			 = 99999;
		int		kmokuNo			 = 0;

		// fieldSetタグ、お気に入り編集画面リンクの出力
		final GUIInfo editFavorite = userInfo.getGUIInfo( "GE0014" );

		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE )
			.append( "<div class=\"expand1\"> " )
			.append( makeTagMenuString( null,null,largeClassify,null,1 ) )
			.append( makeTagMenu( editFavorite,resource.getLabelData( "EDIT" ).getShortLabel(),2 ) )		// 5.5.2.5 (2012/05/21) イメージアイコン対応
			.append( BR );

		String thisClassify = null;
		String gamenId = null;
		FavoriteGUIData favoriteGui = null;
		// 4.3.4.4 (2009/01/01) findBugs警告対応
		for( final Map.Entry<String, FavoriteGUIData> entry : favoriteMap.entrySet() ) {
			gamenId = entry.getKey();
			favoriteGui = entry.getValue();
			final GUIInfo guiInfo = userInfo.getGUIInfo( gamenId );

			if( match   != null && !gamenId.matches( match  ) ) { continue; }
			if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }

			thisClassify = favoriteGui.getClassify();
			if( !lastClassify.equals( thisClassify ) ) {
				menuNo++;
				kmokuNo = 0;
				if( expand ) {
					rtn.append( "<div class=\"expand1\" id=\"menu" )
						.append( menuNo ).append( "\">" );
				}
				rtn.append( makeTagMenuString( null,null,thisClassify,thisClassify,2 ) )
					.append( BR );
				if( expand ) {
					rtn.append( "</div>" ).append( CR );
				}
				lastClassify = thisClassify;
			}

			if( expand ) {
				rtn.append( "<div class=\"expand2\" id=\"menu" )
					.append( menuNo ).append( '_' ).append( kmokuNo ).append( "\">" ) ;		// 6.0.2.5 (2014/10/31) char を append する。
			}

			final String linkLabel = favoriteGui.getName();
			rtn.append( makeTagMenu( guiInfo,linkLabel,3 ) )		// 5.5.2.5 (2012/05/21) イメージアイコン対応
				.append( BR );
			if( expand ) {
				rtn.append( "</div>" ).append( CR );
			}
			kmokuNo++;
		}
		rtn.append( FIELD_OUT )
			.append( "</div>" );

		return rtn.toString();
	}

	/**
	 * 折りたたみメニューで、非標準メニューの 表示に使う、"←･･･→" を作成します。
	 *
	 * @param	menuNo	階層番号
	 * @param	kmokuNo	階層項目番号
	 * @param	type	タイプ(1,2限定)
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
	 *
	 * @return  メニュー
	 * @og.rtnNotNull
	 */
	private String makeEllipses( final int menuNo,final int kmokuNo,final int type ) {
		final int tmpType = type-1; // 超暫定対応 4.0.0.0 (2007/10/31)
		final String kmkNo = ( tmpType == 1 ) ? "" : "_" + kmokuNo ;

		// 4.3.3.0 (2008/10/01) inlineStyleがtrueの場合は←･･･→をinlineで表示する
		// String rtn = "<div class=\"expand" + tmpType + " ellipses\" id=\"menu" + menuNo
		//			+ kmkNo  + "\">"
		//			+ makeTagMenuString( null,null,"←･･･→",null,type )
		//			+ BR + "</div>" + CR ;
		String rtn = "<div class=\"expand" + tmpType + " ellipses\" id=\"menu" + menuNo
				+ kmkNo  + "\" ";
		if( inlineStyle ){
			rtn += "style=\"display:inline\" ";
		}
		rtn += ">" + makeTagMenuString( null,null,"←･･･→",null,type )
			+ BR + "</div>" + CR ;

		return rtn ;
	}

	/**
	 * menuType="GROUP" 時に作成するグループメニュー。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
	 * @og.rev 4.0.0.0 (2007/10/05) グループのコードリソースが存在しない場合
	 * @og.rev 4.2.2.0 (2008/05/14) buttonRequestがtrueの場合はTOPへのリンクを表示しない
	 * @og.rev 4.2.2.0 (2008/05/15) グループ絞込解除(GUI_ALL)の表示文字にラベルリソースを使う。
	 * @og.rev 5.0.0.3 (2009/09/22) グループが1件の場合に表示されないバグを修正
	 * @og.rev 5.1.8.0 (2010/07/01) コードリソースのキーが存在しない場合にエラーとなるバグを修正
	 *
	 * @return  メニュー
	 */
	private String makeGroupMenu() {

		// 画面のグループメニューのキャッシュを取得します。
		String groupMenu ;
		final String cacheKey  = HybsSystem.GUI_GR_MENU_CACHE_KEY + href + target ;
		if( cache ) {
			groupMenu = (String)getSessionAttribute( cacheKey );
			if( groupMenu != null ) { return groupMenu; }	// キャッシュを返します。
		}
		else {
			removeSessionAttribute( cacheKey );
		}

		final UserInfo userInfo = getUser();

		final Set<String> groupSet = new LinkedHashSet<String>();

		// そのユーザーで使用できる画面をすべてピックアップします。
		// その上で、読取可能なメニューを含むグループを順番に Set にセットしていきます。
		final GUIInfo[] guiInfos = userInfo.getGUIInfos();
		String gamenId  ;			// 画面ID
		for( int i=0; i<guiInfos.length; i++ ) {
			if( guiInfos[i].isRead() ) {
				gamenId = guiInfos[i].getKey() ;
				if( match   != null && !gamenId.matches( match  ) ) { continue; }
				if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }
				final String[] groups = StringUtil.csv2Array( guiInfos[i].getGroups() );
				for( int j=0; j<groups.length; j++ ) {
					groupSet.add( groups[j] );
				}
			}
		}

		// 5.0.0.3 (2009/09/22)
		// 6.1.1.0 (2015/01/17) refactoring 。size()判定をisEmpty() にすると同時に、if...else を反転する。
//		if( groupSet.size() > 0 ) {
		if( groupSet.isEmpty() ) {
			groupMenu = "";
		}
		else {
			int sideTmpCnt = 1;
			final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );
			rtn.append( "<tr>" );

			final String allMenu = getLabel( "ALL_MENU" ); // 4.2.2.0 (2008/05/15) ALLはラベルリソース使うように変更
			rtn.append( makeTagMenuString( href,target,allMenu,"GUI_ALL",-1 ) );
			if( sideCount > 0 && sideTmpCnt % sideCount == 0 ) { rtn.append("</tr><tr>"); }
			sideTmpCnt++ ;

			final Iterator<String> ite = groupSet.iterator() ;
			final CodeData groupCode = getResource().getCodeData( "GROUPS" ) ;
			if( groupCode != null ) {
				while( ite.hasNext() ) {
					final String group = ite.next();
					final int cdAdrs = groupCode.getAddress( group ) ;
					// 4.0.0.0 (2007/10/05) グループのコードリソースが存在しない場合
					String groupLabel = "";
					if( cdAdrs >= 0 ) { groupLabel = groupCode.getLongLabel( cdAdrs ); }

					// 5.1.8.0 (2010/07/01) コードリソースのキーが存在しない場合にエラーとなるバグを修正
					if( groupLabel.isEmpty() && group != null && group.length() > 0 ) {
						groupLabel = group;
					}

					final String src = XHTMLTag.addUrlEncode( href,"group=" + group );
					rtn.append( makeTagMenuString( src,target,groupLabel,group,-1 ) );
					if( sideCount > 0 && sideTmpCnt % sideCount == 0 ) { rtn.append("</tr><tr>"); }
					sideTmpCnt++ ;
				}
			}

			if( ! imageOnly && ! buttonRequest ) { // 4.2.2.0 (2008/05/14) ボタンメニュー時はトップメニュー
				rtn.append( makeTagMenuString( JSP + "/index.jsp",target,"Top","GUI_TOP",-1 ) );
			}
			rtn.append( "</tr>" );

			// 画面のグループメニューのキャッシュをセットします。
			groupMenu = rtn.toString() ;
		}
//		else {
//			groupMenu = "";
//		}

		if( cache ) {
			setSessionAttribute( cacheKey,groupMenu );
		}

		return groupMenu;
	}

	/**
	 * メニューを表示する為のHTMLを作成します(折り返しJavaScript対応版)。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) GUIInfoの実アドレスのパラメータを考慮する。
	 * @og.rev 4.0.0.0 (2007/10/31) 一旦廃止
	 * @og.rev 5.5.2.3 (2012/05/15) useDivOneLevel 対応
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 * @og.rev 5.5.6.0 (2013/01/07) useDivOneLevelの折り返し方法変更
	 *
	 * @return  メニュー
	 * @og.rtnNotNull
	 */
	private String makeOneLevelMenu() {
		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );

		// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
		if( useDivOneLevel ) { rtn.append("<div id=\"design-onelevel\">"); }

		final UserInfo userInfo = getUser();

		// 4.0.0 (2005/01/31)
		final GUIInfo[] guiInfos = userInfo.getGUIInfos();
		String gamenId  ;			// 画面ID
		int sideTmpCnt = 1;

		boolean isInClassify = false;

		for( int i=0; i<guiInfos.length; i++ ) {
			final int level = guiInfos[i].getLevel();

			if( level == 2 ) {
				if( selClassify.equals( guiInfos[i].getKey() ) ) {
					isInClassify = true;
				}
				else {
					isInClassify = false;
				}
			}

			if( guiInfos[i].isRead() && level == 3 && isInClassify ) {		// 4.0.0 (2005/01/31)
				gamenId = guiInfos[i].getKey() ;
				if( match   != null && !gamenId.matches( match  ) ) { continue; }
				if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }

				final String guiLabel = guiInfos[i].getName();		// 画面ラベル

				final String thisGamenId = getGUIInfoAttri( "KEY" );
				if( gamenId.equals( thisGamenId ) ) {
					// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
					if( useDivOneLevel ) {
						rtn.append("<span class=\"design-onelevel\">").append( guiLabel ).append( "</span>" );
					}
					else {
						rtn.append( '[' ).append( guiLabel ).append( "] " );		// 6.0.2.5 (2014/10/31) char を append する。
					}
				}
				else {
					// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
					if( useDivOneLevel ) {
						// 5.5.2.5 (2012/05/21) イメージアイコン対応
						rtn.append("<span class=\"design-onelevel\">").append( makeTagMenu( guiInfos[i],guiLabel,-3 ) ).append( "</span>" );
					}
					else {
						rtn.append( makeTagMenu( guiInfos[i],guiLabel,-2 ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
					}
				}

				if( sideCount > 0 ) {
					// 全件数をサイドカウント数で割った値が最大行数の場合は、終了する。
					if( sideTmpCnt / sideCount >= maxCellCount ) {
						//						rtn.append("･･･");
						break;
					}
					// 全件数をサイドカウント数で割った余りがゼロの場合は、行ブレイクする。
					// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
					if( sideTmpCnt % sideCount == 0 ) {
						// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
						// 6.0.2.5 (2014/10/31) refactoring: findBugs対応。分岐もコメントアウトする。
				//		if( useDivOneLevel ) {
				//			rtn.append( BR ); // 5.6.0.0 (2013/01/07) BRにする(条件分岐は残しておく)
				//			// rtn.append("<span style=\"clear: both;\" />");
				//		}
				//		else {
							rtn.append( BR );
				//		}
					}
					sideTmpCnt++ ;
				}
			}
		}

		// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
		if( useDivOneLevel ) {
			rtn.append("<span style=\"clear: both;\" /></div>");
		}

		return rtn.toString();
	}

	/**
	 * 既存のページの次にアクセスされる画面郡のリンクを作成します。
	 *
	 * これは、現時点の画面に対して、次にアクセスされる画面の候補を
	 * ピックアップしておく機能です。
	 * 実際には、過去にアクセスされた結果より取得しています。
	 * これは、ONELEVEL と置き換えることになる機能です。
	 *
	 * @og.rev 5.2.3.0 (2010/12/01) NEXTGUI 追加
	 * @og.rev 5.5.2.3 (2012/05/15) useDivOneLevel 対応
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 *
	 * @return  メニュー
	 * @og.rtnNotNull
	 */
	private String makeNextguiMenu() {
		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );

		// 今、アクセスしている画面
		final GUIInfo thisGamen = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY );
		final String[] nextGuis = thisGamen.getNextGuiArray();

		final UserInfo userInfo = getUser();
		int sideTmpCnt = 1;

		// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
		if( useDivOneLevel ) { rtn.append("<div id=\"design-onelevel\">"); }

		GUIInfo guiInfo = null;
		for( int i=0; i<nextGuis.length; i++ ) {
			guiInfo = userInfo.getGUIInfo( nextGuis[i] );
			if( guiInfo == null ) { continue; }		// 存在しない、またはアクセス拒否の場合は、無視する。

			if( guiInfo.isRead() ) {
				final String guiLabel = guiInfo.getName();		// 画面ラベル

				// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
				if( useDivOneLevel ) {
					// 5.5.2.5 (2012/05/21) イメージアイコン対応
					rtn.append("<span class=\"design-onelevel\">").append( makeTagMenu( guiInfo,guiLabel,-3 ) ).append( "</span>" );
				}
				else {
					rtn.append( makeTagMenu( guiInfo,guiLabel,-2 ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
				}

				if( sideCount > 0 ) {
					// 全件数をサイドカウント数で割った値が最大行数の場合は、終了する。
					if( sideTmpCnt / sideCount >= maxCellCount ) {
						break;
					}
					// 全件数をサイドカウント数で割った余りがゼロの場合は、行ブレイクする。
					// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
					if( sideTmpCnt % sideCount == 0 ) {
						if( useDivOneLevel ) {
							rtn.append("<span style=\"clear: both;\" />");
						}
						else {
							rtn.append( BR );
						}
					}
					sideTmpCnt++ ;
				}
			}
		}

		// 5.5.2.3 (2012/05/15) useDivOneLevel 対応
		if( useDivOneLevel ) { rtn.append("</div>"); }

		return rtn.toString();
	}

	/**
	 * メニューを表示する為のHTMLを作成します(マトリクスメニュー)。
	 *
	 * 分類まとめ、クラス色指定、最小行数設定、最大行数設定の機能があります。
	 * 《分類まとめ》 最大行数設定(maxCellCount)に達したセルは、一つ右に新たにセルを作成
	 * します。このとき、ヘッダーの CLASSIFY を同じにして、colspan でまとめます。
	 * 《クラス色指定》ヘッダー毎に 順次 CLR0,CLR1,････ というクラス属性を付与します。
	 * ヘッダーには、MENU_H も出力されています。CLR0 は、大分類ごとに加算されていきますので、
	 * 繰り返して同じ色を指定する場合は、CSSファイルでまとめて指定してください。
	 * 《最小行数設定》minCellCount 属性を指定することで、１セルに含まれる最小行数を指定できます。
	 * これは、セルに入れる &lt;br /&gt; の個数を指定することと同じです。
	 * 《最大行数設定》maxCellCount 属性を指定することで、１セルに含まれる最大行数を指定できます。
	 * 分類まとめでも説明しましたように、最大値をオーバーすると次のセルから書き始めます。
	 *
	 * @og.rev 4.0.0.0 (2005/11/30) 新規追加
	 * @og.rev 4.0.0.0 (2007/10/05) 分類のコードリソースが存在しない場合
	 * @og.rev 5.2.3.0 (2010/12/01) sideCount対応
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 * @og.rev 5.5.5.3 (2012/08/17) th,tdにヘッダの画面IDをクラスとして出力
	 *
	 * @return  マトリクスメニュー
	 * @og.rtnNotNull
	 */
	private String makeMatrixMenu() {
		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );

		final UserInfo userInfo = getUser();

		// 4.0.0 (2005/01/31)
		final GUIInfo[] guiInfos = userInfo.getGUIInfos();
		String gamenId		;			// 画面ID
		String bkClassifyKey	= null;		// 分類コード(旧)
		String bkClassifyName	= null;		// 分類名称(旧)
		int	   cellColorCnt = 0;		// MENU_H に 追加する CLR クラス属性の連番

		String headGuikey = "";				//  5.5.5.3 (2012/08/17) ヘッダのgamenId

//		StringBuilder rtnH = null;		// 分類部分の出力用
//		StringBuilder rtnB = null;		// 実画面のリンク部分の出力用
		final StringBuilder rtnH = new StringBuilder( BUFFER_LARGE );
		final StringBuilder rtnB = new StringBuilder( BUFFER_LARGE );

		int level ;
		int preLevel = 0;

		int lineTmpCnt = 0;				// セル中の行カウント
		int cellTmpCnt = 0;				// 1つの分類中のセルカウント
		int cellTmpAllCnt = 0;			// 5.2.3.0 (2010/12/01) 該当行のセルカウント

		boolean isInClassify = false;	// 分類の中か？
		boolean isChangeLevel = false;	// 直リンク用(無理やり通常画面の階層として扱うので)
		boolean isDummyMENU_S = false;	// 5.2.3.0 (2010/12/01) sideCountブレーク時の大分類領域の出力可否

		for( int i=0; i<guiInfos.length; i++ ) {
			if( guiInfos[i].isRead() ) {		// 4.0.0 (2005/01/31)
				gamenId = guiInfos[i].getKey() ;
				if( match   != null && !gamenId.matches( match  ) ) { continue; }
				if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }

				// 処理すべき画面かのチェック
				final int guiFlg = guiCheck( guiInfos, i );
				if( guiFlg == 0 ) { continue; }

				level = guiInfos[i].getLevel();		// 4.0.0 (2005/01/31)

				// 大分類が来るまでは処理しない
				if( level > 1 && rtnH == null ) { continue; }

				// 直リンクの場合、無理やり通常画面に変換
				if( level == 2 && guiInfos[i].getAddress() != null && guiInfos[i].getAddress().length() != 0 ) {
					level = 3;
					if( !isChangeLevel ) {
						isChangeLevel = true;
						isInClassify = false;
					}
				}
				else {
					isChangeLevel = false;
				}

				// 分類のブレイク処理
				if( ( preLevel >= 3 && level < 3 ) || !isInClassify ) {
					if(  lineTmpCnt != 0 ) {
						for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
						rtnB.append( "</td>" ).append( CR );
					}

					if( bkClassifyKey != null ) {
						rtnH.append( "<th colspan=\"" ).append( cellTmpCnt ).append( "\" class=\"MENU_H CLR" )
							.append( cellColorCnt ).append( ' ' ).append( headGuikey ).append( "\">" ); // 5.5.5.3 (2012/08/17)
						if( "_SPACE".equals( bkClassifyKey ) ) {
							rtnH.append( '　' );		// 6.0.2.5 (2014/10/31) char を append する。
						}
						else {
							rtnH.append( makeTagMenuString( null,null,bkClassifyName,bkClassifyKey,-3 ) );
						}
						rtnH.append( "</th>" );

						// 5.2.3.0 (2010/12/01) sideCount によるセルの改行
						cellTmpAllCnt += cellTmpCnt;
						if( sideCount > 0 && cellTmpAllCnt >= sideCount ) {
							rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );

//							rtnH = new StringBuilder( BUFFER_LARGE );
//							rtnB = new StringBuilder( BUFFER_LARGE );
							rtnH.setLength(0);		// 6.1.0.0 (2014/12/26) refactoring
							rtnB.setLength(0);		// 6.1.0.0 (2014/12/26) refactoring
							cellTmpAllCnt = 0;
							isDummyMENU_S = true;	// 出力予約
						}
					}

					bkClassifyKey = null;
					isInClassify = false;
					lineTmpCnt = 0;
					cellTmpCnt = 0;
				}

				// 大分類(フィールドメニュー)
				if( level == 1 ) {
					headGuikey = gamenId; // 5.5.5.3 (2012/08/17)
					if( preLevel > 0 ) {
						cellColorCnt++ ;
						rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );
					}

					rtn.append( "<tr><td rowspan=\"2\" class=\"MENU_S "+ headGuikey +"\">" )	// 5.5.5.3 (2012/08/17)
						.append( makeTagMenuString( null,null,guiInfos[i].getName(),gamenId,-3 ) )
						.append( "</td>" ).append( CR );

//					rtnH = new StringBuilder( BUFFER_LARGE );
//					rtnB = new StringBuilder( BUFFER_LARGE );
					rtnH.setLength(0);		// 6.1.0.0 (2014/12/26) refactoring
					rtnB.setLength(0);		// 6.1.0.0 (2014/12/26) refactoring
					cellTmpAllCnt = 0;		// 5.2.3.0 (2010/12/01)
					isDummyMENU_S = false;	// 5.2.3.0 (2010/12/01) 出力予約解除
				}
				// 分類(直リンクの場合は、level=3で処理)
				else if( level == 2 ) {
					isInClassify = true;
					bkClassifyKey = guiInfos[i].getKey();
					bkClassifyName = guiInfos[i].getName();
				}
				// 通常メニュー・隠しメニュー
				else if( level >= 3 ) {

					// 元のMENU_Sに戻り、rowspan を書き換える・・・のが邪魔くさいので、td のみ出力しておく。
					// 5.2.3.0 (2010/12/01) sideCount によるセルの改行
					if( isDummyMENU_S ) {
						rtn.append( "<tr><td rowspan=\"2\" class=\"MENU_S " )
							.append( headGuikey ).append( "\"></td>" )	// 5.5.5.3 (2012/08/17)
							.append( CR );
						isDummyMENU_S = false;		// 出力予約解除
					}

					// 画面ID="HYBS_BR"の場合は、セルを変える。
					if( "HYBS_BR".equals( guiInfos[i].getKey() ) ) {
						if( lineTmpCnt != 0 ) {
							for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
							lineTmpCnt = 0;
						}
					}
					// 通常画面
					else {
						if( lineTmpCnt == 0 ) {
							rtnB.append( "<td class=\"MENU_B " + headGuikey + "\">" ); // 5.5.5.3 (2012/08/17)
							cellTmpCnt++;
						}

						rtnB.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),-3 ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
						lineTmpCnt++;

						if( lineTmpCnt >= maxCellCount ) {
							rtnB.append( "</td>" );
							lineTmpCnt = 0;
						}
						else {
							rtnB.append( BR );
						}

						// 分類の中に入っていない通常画面
						if( !isInClassify ) {
							bkClassifyKey = "_SPACE";
							isInClassify = true;
						}
					}
				}

				preLevel = level;
			}

		}

		// 終了処理
		for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
		rtnB.append( "</td>" ).append( CR );

		if( bkClassifyKey != null ) {
//			rtnH.append( "<th colspan=\"" + cellTmpCnt + "\" class=\"MENU_H CLR" + cellColorCnt + " " + headGuikey + "\">" ); // 5.5.5.3 (2012/08/17)
			rtnH.append( "<th colspan=\"" ).append( cellTmpCnt )
				.append( "\" class=\"MENU_H CLR" ) .append(  cellColorCnt )
				.append( ' ' ).append(  headGuikey ).append( "\">" )			// 5.5.5.3 (2012/08/17)
				.append( makeTagMenuString( null,null,bkClassifyName,bkClassifyKey,-3 ) )
				.append( "</th>" );
		}

		rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );

		return rtn.toString();
	}

	/**
	 * メニューを表示する為のHTMLを作成します(マトリクスメニュー2)。
	 *
	 * マトリクスメニューをベースとした特別バージョンです。
	 * 通常のマトリクスメニューからの変更点は以下の通りです。
	 * ①大分類が表示されない
	 *   ⇒ 変わりに、1行に表示されているセル数がsideCountで指定した数を超えた場合に
	 *      自動的に改行されます。
	 * ②画面リンクのターゲット
	 *   ⇒ 自分自身のフレームに対してリンク先を表示します。
	 *      リンク先は、通常メニュー構成ですが左メニューには該当する小分類の画面しか表示されません。
	 * ③小分類でのリンク
	 *   ⇒ 小分類をクリックした際に、通常のメニュー構成画面にリンクします。
	 *      但し、②と同様に左メニューには該当する小分類の画面しか表示されません。
	 *
	 * @og.rev 4.2.1.0 (2008/04/01) 新規追加
	 * @og.rev 4.2.1.1 (2008/05/02) カテゴリーリンクで一番上の画面のモードが-wとなっている場合に、
	 *                               その画面が立ち上がってしまうバグを修正
	 * @og.rev 4.2.2.0 (2008/05/14) buttonRequestの付加をmakeTagMenuString()に変更
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(makeTagMenuのパラメータ変更)
	 * @og.rev 5.5.5.3 (2012/08/17) ヘッダ部のgamenIdをth,tdのクラスに追加
	 *
	 * @return  マトリクスメニュー
	 * @og.rtnNotNull
	 */
	private String makeMatrixMenu2() {
		final StringBuilder rtn = new StringBuilder( BUFFER_LARGE );

		final UserInfo userInfo = getUser();

		final GUIInfo[] guiInfos = userInfo.getGUIInfos();
		String gamenId		;			// 画面ID
		String bkClassifyKey	= null;		// 分類コード(旧)
		String nextKey			= null;		// 分類ボタンを押した時に最初の画面が開くようにする
		String bkClassifyName	= null;		// 分類名称(旧)
		final int	   cellColorCnt = 0;		// MENU_H に 追加する CLR クラス属性の連番

		final StringBuilder rtnH = new StringBuilder( BUFFER_LARGE );		// 分類部分の出力用
		final StringBuilder rtnB = new StringBuilder( BUFFER_LARGE );		// 実画面のリンク部分の出力用

		int level ;
		int preLevel = 0;

		int lineTmpCnt = 0;				// セル中の行カウント
		int cellTmpCnt = 0;				// 1つの分類中のセルカウント
		int cellTmpAllCnt = 0;			// 該当行のセルカウント

		boolean isInClassify = false;	// 分類の中か？
		boolean isChangeLevel = false;	// 直リンク用(無理やり通常画面の階層として扱うので)

		String classifyHref = null;		// 小分類のリンク

		String headGuikey = "" ;		// 5.5.5.3 (2012/08/17)

		rtn.append( "<tr>" ).append( CR );

		for( int i=0; i<guiInfos.length; i++ ) {
			if( guiInfos[i].isRead() ) {		// 4.0.0 (2005/01/31)
				gamenId = guiInfos[i].getKey() ;
				if( match   != null && !gamenId.matches( match  ) ) { continue; }
				if( unmatch != null && gamenId.matches( unmatch ) ) { continue; }

				// 処理すべき画面かのチェック
				final int guiFlg = guiCheck( guiInfos, i );
				if( guiFlg == 0 ) { continue; }

				level = guiInfos[i].getLevel();		// 4.0.0 (2005/01/31)

				// 直リンクの場合、無理やり通常画面に変換
				if( level == 2 && guiInfos[i].getAddress() != null && guiInfos[i].getAddress().length() != 0 ) {
					level = 3;
					if( !isChangeLevel ) {
						isChangeLevel = true;
						isInClassify = false;
					}
				}
				else {
					isChangeLevel = false;
				}

				// 分類のブレイク処理
				if( ( preLevel >= 3 && level < 3 ) || !isInClassify ) {
					if(  lineTmpCnt != 0 ) {
						for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
						//rtnB.append( "</td>" ).append( CR ); // 4.2.1.0 (2008/04/03) リスト
						rtnB.append( "</ul></div></td>" ).append( CR );
					}

					if( bkClassifyKey != null ) {
						// 6.0.2.5 (2014/10/31) char を append する。
//						rtnH.append( "<th colspan=\"" + cellTmpCnt + "\" class=\"MENU_H CLR" + cellColorCnt + " " + headGuikey + "\">" ); // 5.5.5.3 (2012/08/17)
						rtnH.append( "<th colspan=\"" ).append( cellTmpCnt ).append( "\" class=\"MENU_H CLR" )
							.append( cellColorCnt ).append( ' ' ).append( headGuikey ).append( "\">" );		// 5.5.5.3 (2012/08/17)
						if( "_SPACE".equals( bkClassifyKey ) ) {
							rtnH.append( '　' );					// 6.0.2.5 (2014/10/31) refactoring
						}
						else {
							classifyHref = JSP + "/index.jsp?classify=" + bkClassifyKey;
							//	4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
							classifyHref += "&amp;GAMENID="+nextKey; // ボタンを押した場合に最初の画面が現れる
							rtnH.append( makeTagMenuString( classifyHref,"_self",bkClassifyName,bkClassifyKey,-3 ) );
						}
						rtnH.append( "</th>" );

						cellTmpAllCnt += cellTmpCnt;

						if( sideCount > 0 && cellTmpAllCnt >= sideCount ) {
							rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );

//							rtnH = new StringBuilder( BUFFER_LARGE );
//							rtnB = new StringBuilder( BUFFER_LARGE );
							rtnH.setLength(0);				// 6.1.0.0 (2014/12/26) refactoring
							rtnB.setLength(0);				// 6.1.0.0 (2014/12/26) refactoring
							cellTmpAllCnt = 0;
						}
					}

					bkClassifyKey = null;
					nextKey		  = null;
					isInClassify = false;
					lineTmpCnt = 0;
					cellTmpCnt = 0;
				}

				// 5.5.5.3 (2012/08/17) 大分類(フィールドメニュー) 
				if( level == 1 ) {
					headGuikey = gamenId; 
				}
				// 分類(直リンクの場合は、level=3で処理)
				else if( level == 2 ) {
					isInClassify = true;
					if( guiInfos[i].isPulldown() ){ // 4.3.3.0 (2008/10/01) プルダウン化チェック
						excludeButton = true;
					}
					else{
						excludeButton = false;
					}
					bkClassifyKey = guiInfos[i].getKey();
					bkClassifyName = guiInfos[i].getName();
					for( int j=i+1; j<guiInfos.length; j++ ) {
						if( guiInfos[j] != null && guiInfos[j].isRead() ) {
							nextKey = guiInfos[j].getKey();
							break;
						}
					}
				}
				// 通常メニュー・隠しメニュー
				else if( level >= 3 ) {

					// 画面ID="HYBS_BR"の場合は、セルを変える。
					if( "HYBS_BR".equals( guiInfos[i].getKey() ) ) {
						if( lineTmpCnt != 0 ) {
							for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
							lineTmpCnt = 0;
						}
					}
					// 通常画面
					else {
						if( lineTmpCnt == 0 ) {
							// rtnB.append( "<td class=\"MENU_B\">" );
							// 4.2.1.0 (2008/04/03) リンクをリスト形式で出す案
							rtnB.append( "<td class=\"MENU_B "+ headGuikey +"\"> <div class=\"gamen-menu-wrap\"><ul class=\"gamen-menu\">" ); // 5.5.5.3 (2012/08/17)
							cellTmpCnt++;
						}

						// 5.5.2.5 (2012/05/21) リストは、イメージを設定するので、メソッド側で付与します。
						rtnB.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),-4 ) );		// 5.5.2.5 (2012/05/21) イメージアイコン対応
						lineTmpCnt++;

						if( lineTmpCnt >= maxCellCount ) {
							// rtnB.append( "</td>" );
							rtnB.append( " </ul> </div></td>" ); // 4.2.1.0 (008/04/03) リスト
							lineTmpCnt = 0;
						}
				//		else {
				//			rtnB.append( BR ); // 4.2.1.0 (2008/04/03) リスト。改行不要。
				//		}

						// 分類の中に入っていない通常画面
						if( !isInClassify ) {
							bkClassifyKey = "_SPACE";
							isInClassify = true;
						}
					}
				}

				preLevel = level;
			}

		}

		// 終了処理
		// 4.3.3.0 (2008/10/01) 終了処理は0の時行はない
		if( lineTmpCnt != 0){
			for( int j=lineTmpCnt; j<minCellCount-1; j++ ) { rtnB.append( BR ); }
			rtnB.append( "</td>" ).append( CR );
		}

		if( bkClassifyKey != null ) {
			rtnH.append( "<th colspan=\"" + cellTmpCnt + "\" class=\"MENU_H CLR" + cellColorCnt + " " + headGuikey + "\">" ); // 5.5.5.3 (2012/08/17)

			classifyHref = JSP + "/index.jsp?classify=" + bkClassifyKey;
			//	4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
			classifyHref += "&amp;GAMENID="+nextKey;
			rtnH.append( makeTagMenuString( classifyHref,"_self",bkClassifyName,bkClassifyKey,-3 ) );
			rtnH.append( "</th>" );
		}

		rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );

		return rtn.toString();
	}

	/**
	 * タイプに応じたメニューを表示する為の文字列を作成します。
	 * 引数の GUIInfo より、アクセスすべきメニューのhrefと、targetを求めます。
	 * type = -3 (マトリクスメニュー) の場合だけ、タグ属性の target を使用します。
	 *
	 * 　0:フィールドセットメニュー
	 * 　1:トップ階層(【分類名称】)
	 * 　2:選択階層(通常の折りたたみメニュー)
	 * 　3:選択非表示(通常は、隠してあります)
	 * 　-1:グループ分類メニュー(class="GUI_GRP"のtdタグで囲われます)
	 * 　-2:ラインメニュー([画面名称]　)
	 * 　-3:マトリクスメニュー(一覧)
	 * 　-4:マトリクスメニュー２(一覧)
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録(makeTopMenu、makeSecondMenuの置き換え)
	 * @og.rev 4.1.0.1 (2008/01/22) アドレスが[..]から始まる画面はマルチセッションチェックを行わない。
	 * @og.rev 4.2.1.0 (2008/04/01) マトリクスメニュー2用のタグ作成処理追加
	 * @og.rev 4.2.1.0 (2008/04/17) マトリクス２からの遷移先でボタンメニューにする処理
	 * @og.rev 4.2.2.0 (2008/05/14) buttonRequestの付加をmakeTagMenuString()に変更
	 * @og.rev 4.3.3.7 (2008/11/22) https対応
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(画面ID単位ではなく、IMAGE_KEYパラメータで指定可能にする。)
	 * @og.rev 5.7.8.1 (2014/07/18) realAddress の "/" 対応
	 *
	 * @param	guiInfo GUIInfoリンク
	 * @param	bodyIn	BODY部(表示)
	 * @param	type	タイプ
	 *
	 * @return  階層別メニュー文字列
	 * @og.rtnNotNull
	 */
	private String makeTagMenu( final GUIInfo guiInfo, final String bodyIn, final int type ) {

		String href		 = null;
		String tmpTarget = null;
		final String gamenId   = guiInfo.getKey();	// 5.5.2.5 (2012/05/21) イメージアイコン対応

		if( guiInfo.getAddress() != null && guiInfo.getAddress().length() > 0 ) {
			final String readAdrs = guiInfo.getRealAddress( "index.jsp" );
			final String reqParam = getRequestParameter( guiInfo.getParam() );
			href = XHTMLTag.addUrlEncode( readAdrs,reqParam );
			tmpTarget = guiInfo.getTarget();

			// 4.3.3.7 (2008/11/22) https対応
			// 5.7.8.1 (2014/07/18) realAddress の "/" 対応
			final String kblink = guiInfo.getKblink();
//			if( ! readAdrs.startsWith( "http://" ) && ! readAdrs.startsWith( "https://" )) {
			if( ! readAdrs.startsWith( "http://" ) && ! readAdrs.startsWith( "https://" ) && !"/".equals( kblink ) ) {
				if( type == -3 ) {
					tmpTarget = target;
//					final String param = getRequestParameter( guiInfo.getParam() );
//					href = XHTMLTag.addUrlEncode( JSP + "/result.jsp",param );
					href = XHTMLTag.addUrlEncode( JSP + "/result.jsp",reqParam );	// 6.2.0.0 (2015/02/27)
				}
				else if( type == -4 ) {		// 4.2.1.0 (2008/04/01)
					tmpTarget = "_self";
//					final String param = getRequestParameter( guiInfo.getParam() );
//					href = XHTMLTag.addUrlEncode( JSP + "/index.jsp",param );
					href = XHTMLTag.addUrlEncode( JSP + "/index.jsp",reqParam );	// 6.2.0.0 (2015/02/27)
					href = XHTMLTag.addUrlEncode( href,"classify=" + guiInfo.getClassify() );
				}
				href = XHTMLTag.addUrlEncode( href,"GAMENID=" + gamenId );			// 5.5.2.5 (2012/05/21) せっかくなので、利用する。

				// 3.8.0.0 (2005/06/07) 同一セッションでのマルチ起動対策を行います。
				// 4.1.0.1 (2008/01/22) アドレスが[..]から始まる画面はマルチセッションチェックを行いません。
				// 4.1.0.1 (2008/04/01) マトリクスメニュー2はマルチセッションチェックを行いません。
				if( multiSessionCheck && !guiInfo.getAddress().startsWith( ".." ) && type != -4 ) {
					href = XHTMLTag.addUrlEncode( href,mscKey );
				}
			}
		}
		// 5.5.2.5 (2012/05/21) イメージアイコン対応(画面ID単位ではなく、IMAGE_KEYパラメータで指定可能にする。)
		final String imgKey = guiInfo.getImageKey() ;
		return makeTagMenuString( href,tmpTarget,bodyIn,gamenId,imgKey,type ) ;
	}

	/**
	 * タイプに応じたメニューを表示する為の文字列を作成します。
	 * 従来からのメソッドの引数のままで、新しい gamenId は、imgKey をそのままセットします。
	 *
	 * 　0:フィールドセットメニュー
	 * 　1:トップ階層(【分類名称】)
	 * 　2:選択階層(通常の折りたたみメニュー)
	 * 　3:選択非表示(通常は、隠してあります)
	 * 　-1:グループ分類メニュー(class="GUI_GRP"のtdタグで囲われます)
	 * 　-2:ラインメニュー([画面名称]　)
	 * 　-3:マトリクスメニュー(一覧)
	 * 　-4:マトリクスメニュー2(一覧)
	 *
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(画面ID単位ではなく、IMAGE_KEYパラメータで指定可能にする。)
	 *
	 * @param	href	リンク
	 * @param	target	ターゲット
	 * @param	bodyIn	BODY部(表示)
	 * @param	imgKey	imageファイルの検索キー
	 * @param	type	タイプ
	 *
	 * @return  階層別メニュー文字列
	 * @og.rtnNotNull
	 */
	private String makeTagMenuString( final String href,final String target, final String bodyIn,
								final String imgKey,final int type ) {
		return makeTagMenuString( href,target,bodyIn,imgKey,imgKey,type );	// gamenId の代わりに、imgKey をセットする。
	}

	/**
	 * タイプに応じたメニューを表示する為の文字列を作成します。
	 *
	 *   ・0:フィールドセットメニュー
	 *   ・1:トップ階層(【分類名称】)
	 *   ・2:選択階層(通常の折りたたみメニュー)
	 *   ・3:選択非表示(通常は、隠してあります)
	 *   ・-1:グループ分類メニュー(class="GUI_GRP"のtdタグで囲われます)
	 *   ・-2:ラインメニュー([画面名称]　)
	 *   ・-3:マトリクスメニュー(一覧)
	 *   ・-4:マトリクスメニュー2(一覧)
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録(makeTopMenu、makeSecondMenuの置き換え)
	 * @og.rev 4.0.0.0 (2007/11/28) switch文の中の二重コードを統一します。
	 * @og.rev 4.2.1.0 (2008/04/01) ボタン形式のリンク対応、マトリクスメニュー2対応
	 * @og.rev 4.2.2.0 (2008/05/14) buttonRequestの付加をここで行うようにする
	 * @og.rev 4.3.5.0 (2009/02/01) switch の case で重複項目をまとめます。
	 * @og.rev 5.1.4.0 (2010/03/01) onClick,onMouseOver,onMouseOutの処理は、外部のJavaScriptファイルで記述します。
	 * @og.rev 5.1.8.0 (2010/07/01) コードリソースのキーが存在しない場合にエラーとなるバグを修正
	 * @og.rev 5.3.9.0 (2011/09/01) メニューでのヘルプアイコン対応
	 * @og.rev 5.4.4.4 (2012/02/15) ヘルプアイコンはimgKeyがnullでない場合のみとする
	 * @og.rev 5.5.0.4 (2012/03/14) FAQ対応
	 * @og.rev 5.5.2.5 (2012/05/21) イメージアイコン対応(画面ID単位ではなく、IMAGE_KEYパラメータで指定可能にする。)
	 * @og.rev 5.5.4.2 (2012/07/13) JSP(/gf/jsp/) ではなく、CNTX(gf)＋MENU_IMG(/jsp/menuImage/) で処理するように変更
	 * @og.rev 6.2.2.0 (2015/03/27) BRと\nを相互に変換する処理を追加
	 *
	 * @param	href	リンク
	 * @param	target	ターゲット
	 * @param	bodyIn	BODY部(表示)
	 * @param	gamenId	画面ID/グループIDなど
	 * @param	imgKey	imageファイルの検索キー
	 * @param	type	タイプ
	 *
	 * @return  階層別メニュー文字列
	 * @og.rtnNotNull
	 */
	private String makeTagMenuString( final String href,final String target, final String bodyIn,
								final String gamenId,final String imgKey,final int type ) {

		final String body ;
		String listStyle = "<li>";
		final String imgFile = imgFileMap.getFilename( imgKey );			// 5.5.2.5 (2012/05/21) 属性名変更
		if( imgFile != null ) {
			if( imageOnly ) {
//				body = "<img src=\"/" + CNTX + MENU_IMG + imgFile + "\" title=\"" + bodyIn + "\" />" ;
				body = "<img src=\"/" + CNTX + MENU_IMG + imgFile + "\" title=\""
							 + StringUtil.br2YenN( bodyIn,true ) + "\" />" ;
			}
			// 5.5.2.5 (2012/05/21) -4(MATRIX Menu2)の場合は、<li style="list-style:url(画像URL);"> で処理する。
			else if( type == -4 ) {
				listStyle = "<li style=\"list-style:url(/" + CNTX + MENU_IMG + imgFile + ");\">" ;
				body = bodyIn ;
			}
			else {
				body = "<img src=\"/" + CNTX + MENU_IMG + imgFile + "\"/>" + bodyIn ;
			}
		}
		else {
			if( imageOnly && bodyIn != null && bodyIn.length() > 0 ) {
//				body = "<span title=\"" + bodyIn + "\" >" + bodyIn.charAt(0) + "</span>" ;
				body = "<span title=\"" + StringUtil.br2YenN( bodyIn,true ) + "\" >" + bodyIn.charAt(0) + "</span>" ;
			}
			else {
				body = bodyIn ;
			}
		}

		final String in ;
		if( href != null ) {
//			TagBuffer tag = null;
			if( useButton ) { // 4.2.1.0 (2008/04/01)
				// 6.1.1.0 (2015/01/17) TagBufferの連結記述
				in = new TagBuffer( "button" )
						.add( "onclick"	, "top."+target+".location.href=\'"+ href + "\';" )		// 5.1.4.0 (2010/03/01)
						.add( "class"	, "buttonMenu" ) 		// 4.2.1.0 (2008/04/03) classを追加
						.add( "id"		, gamenId )				// 5.5.2.5 (2012/05/21) idを本当の画面IDで設定
						.addBody( body )
						.makeTag();

//				tag = new TagBuffer( "button" );
//				final String onClick = "top."+target+".location.href=\'"+ href + "\';";
//				// 5.1.4.0 (2010/03/01)
//				tag.add( "onclick" , onClick );
//				tag.add( "class" , "buttonMenu" ); 	// 4.2.1.0 (2008/04/03) classを追加
//				tag.add( "id" , gamenId );			// 5.5.2.5 (2012/05/21) idを本当の画面IDで設定
//				tag.setBody( body );
			}
			else {
				// 4.2.2.0 (2008/05/14) ボタン式にする場合はリクエスト変数付加
				// 4.3.3.0 (2008/10/01) 強制プルダウン化の場合はfalseで渡す(excludeButton の値の反転)
				final String href2 = buttonRequest 
											? XHTMLTag.addUrlEncode( href,"buttonRequest=" + !excludeButton)
											: href ;
				// 6.1.1.0 (2015/01/17) TagBufferの連結記述
				in = new TagBuffer( "a" )
						.add( "class"	, "buttonClassify" , buttonRequest )
						.add( "href"	, href2 )
						.add( "target"	, target )
						.addBody( body )
						.makeTag();

//				tag = new TagBuffer( "a" );
//
//				String href2 = href;
//				if( buttonRequest ){ // 4.2.2.0 (2008/05/14) ボタン式にする場合はリクエスト変数付加
//					if ( excludeButton ){ //4.3.3.0 (2008/10/01) 強制プルダウン化の場合はfalseで渡す
//						href2 = XHTMLTag.addUrlEncode( href2,"buttonRequest=false");
//					}
//					else{
//						href2 = XHTMLTag.addUrlEncode( href2,"buttonRequest=true");
//					}
//					tag.add( "class" , "buttonClassify" );
//				}
//
//				tag.add( "href"		, href2 );
//				tag.add( "target"	, target );
//				tag.setBody( body );
			}
//			in = tag.makeTag();
		}
		else {
			in = body;
		}

		String rtn = null;
		switch( type ) {
			case 1: rtn = FIELD_IN + "<legend>" + in + "</legend>" ; break;	// 大分類
			case 2: rtn = "【"	+ in + "】" ; break;	// 小分類・直リンク
			case 3: 									// 通常メニュー
			case 4: rtn = "　"	+ in 		; break;	// 隠れメニュー
			case -1: rtn = "<td class=\"MENU_G " + gamenId + "\">" + in + "</td>" ; break;	// 5.5.2.5 (2012/05/21) グループメニュー
			case -2: rtn = "["	+ in + "] "; break;		// ラインメニュー
			// 4.3.5.0 (2009/02/01) switch の case で重複項目をまとめます。
			//case -4: rtn = "■ " + in		; break;
			// 5.5.2.5 (2012/05/21) listStyle の適用。type == -4 のみで適用するので、-3 は、そのままセットする。
			case -3: rtn = in				; break;				// マトリクスメニュー
			case -4: rtn = listStyle + in + "</li>"  ; break;		// 4.2.1.0 (2008/04/03) リスト。リストのため■はいらない
			default :
				rtn = "X_" + in ;  break;					 		// 6.0.2.5 (2014/10/31) break追記
		}

		// 5.5.2.5 (2012/05/21) imgKeyではなく、gamenIdがnullの場合は処理しないに変更、faqLinkFormat 廃止。
		if( gamenId != null && helpMap != null && helpMap.exists( gamenId ) ) {
			if( type == 2 ) {
				rtn = rtn + makeHelpLink( gamenId );
			}
			else if( type == -3 ) {
				rtn = "<table><tr><td rowspan=\"2\">" + rtn + "</td><td>"
						+ makeHelpLink( gamenId ) + "</td></tr><tr><td></td></tr></table>";
			}
		}

		return rtn ;
	}

	/**
	 * 【TAG】作成するメニューの種類[NORMAL/GROUP/ONELEVEL/NEXTGUI/MATRIX/MATRIX2]を指定します(初期値:NORMAL)。
	 *
	 * @og.tag
	 * 作成するメニューには、複数の種類があります。
	 * <table border="1" frame="box" rules="all" >
	 *   <caption>メニューの種類</caption>
	 *   <tr><th>種類    </th><th>説明  </th></tr>
	 *   <tr><td>NORMAL  </td><td>通常の階層メニュー</td></tr>
	 *   <tr><td>GROUP   </td><td>GROUPのみを取り出してリンクを作成します。(topMenuに利用)</td></tr>
	 *   <tr><td>ONELEVEL</td><td>指定のclassify のメニューのみを取り出してリンクを作成します。(lineMenuに利用)</td></tr>
	 *   <tr><td>NEXTGUI </td><td>既存のページの次にアクセスされる画面郡のリンクを作成します。</td></tr>
	 *   <tr><td>MATRIX  </td><td>一覧表形式のメニューを作成します。(大分類付きマルチメニュー)</td></tr>
	 *   <tr><td>MATRIX2 </td><td>一覧表形式のメニューを作成します。(大分類なしボタンメニュー)</td></tr>
	 * </table>
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 * @og.rev 5.2.3.0 (2010/12/01) NEXTGUI 追加
	 *
	 * @param	type メニューの種類 [NORMAL/GROUP/ONELEVEL/NEXTGUI/MATRIX/MATRIX2]
	 */
	public void setMenuType( final String type ) {
		menuType = nval( getRequestParameter( type ),MENU_TYPE[0] );
		if( ! check( menuType, MENU_TYPE ) ) {
			final String errMsg = "menuType に、指定できない種類の文字が指定されました。menuType=["
						+ menuType + "]" + CR
						+ "以下の中から、選択してください。["
						+ StringUtil.array2csv( MENU_TYPE ) + "]" + CR;
			throw new HybsSystemException( errMsg );
		}
	}

	/**
	 * 【TAG】折り返しメニューを構築するかどうかを指定します(初期値:true)。
	 *
	 * @og.tag
	 * trueを設定すると、JavaScriptによる折り返しメニューを構築します。
	 * false の場合は、通常のHTMLのみで、階層メニューを構築します。
	 * 初期値は、true(折り返しメニュー)です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	flag 折り返しメニュー [true:する/false:しない]
	 */
	public void setExpand( final String flag ) {
		expand = nval( getRequestParameter( flag ),expand );
	}

	/**
	 * 【TAG】表示対象となるグループをCSV形式で指定します。
	 *
	 * @og.tag
	 * メニューの表示対象グループをCSV形式で複数指定できます。
	 * 指定のグループのメニューだけが、表示対象になります。
	 * メニューにも、複数のグループを指定できるため、１グループの指定で、
	 * 複数のくくりを表示することも可能です。
	 * グループを指定しない場合は、全グループが対象になります。
	 * また、メニュー側にグループ指定がない場合は、グループ指定に
	 * 関係なく、対象になります。
	 * 初期値は、未指定(全メニューが対象)です。
	 * 分解方法は、通常のパラメータ取得後に、CSV分解します。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	menuGroups 表示対象グループ (CSV形式)
	 */
	public void setGroups( final String menuGroups ) {
		groups = StringUtil.csv2Array( getRequestParameter( menuGroups ) );
		if( groups.length == 0 ) { groups = null; }
	}

	/**
	 * 【TAG】表示対象となる分類(classify)を指定します。
	 *
	 * @og.tag
	 * メニューの表示対象となる分類(classify)を指定することで、一まとまりの
	 * メニューを作成します。これは、３段階メニューの最終メニューを求める場合に
	 * 指定します。
	 * 最終メニューは、画面上部に設ける予定のメニューで、上下フレーム分割での
	 * 運用時に使用します。
	 * 分類の指定がない場合は、すべてが表示対象になります。
	 * 初期値は、未指定(全メニューが対象)です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	classify 表示対象分類 (classify)
	 */
	public void setClassify( final String classify ) {
		selClassify = nval( getRequestParameter( classify ),selClassify );
	}

	/**
	 * 【TAG】グループメニューの表示対象となるソース名(href)を指定します(初期値:menu.jsp)。
	 *
	 * @og.tag
	 * GROUPメニューの表示対象となるソース名(href)を指定することで、
	 * サブメニューを自分自身のフレームに対して割り当てるのか、フレームを分けて
	 * 管理するのかを自由に設定できます。
	 * 初期値は、menu.jspです。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	inHref 表示対象ソース名 (href)
	 */
	public void setHref( final String inHref ) {
		href = nval( getRequestParameter( inHref ),href );
	}

	/**
	 * 【TAG】グループメニューの表示対象となるフレーム名(target)を指定します(初期値:MENU)。
	 *
	 * @og.tag
	 * GROUPメニューの表示対象となるフレーム名(target)を指定することで、
	 * サブメニューを自分自身のフレームに対して割り当てるのか、フレームを分けて
	 * 管理するのかを自由に設定できます。
	 * フレーム分割を行うと、変更箇所は、サブメニューのみになる為、動きに無駄が
	 * なくなりますが、グループメニューの大きさが固定されてしまいます。
	 * 自分自身にすると、グループメニューとサブメニューを一つのフレームに
	 * 入れることで、更新時の画面のちらつきは発生しますが、無駄なスペースは
	 * 省くことが可能になります。
	 * 初期値は、MENU(通常のメニューフレーム)です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	inTarget 表示対象フレーム名 (target)
	 */
	public void setTarget( final String inTarget ) {
		target = nval( getRequestParameter( inTarget ),target );
	}

	/**
	 * 【TAG】グループメニューの表示に、画像のみかどうか[true:画像のみ/false:画像＋ラベル]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * GROUPメニューの表示対象として、jsp/menuImage 以下に グループ名と同一の
	 * 画像ファイルが存在する場合は、画像を使用します。
	 * このフラグを、true に設定すると、画像のみを使用します。
	 * false の場合は、画像＋グループ名のラベルを使用します。
	 * 画像のみの場合でも、title 属性にグループ名のラベルをセットしますので、
	 * マウスカーソルをオーバーすれば、名称がTips表示されます。
	 * 画像が存在しない場合に、true(画像のみ)に設定した場合は、ラベルの最初の１文字
	 * のみを出力します。
	 * 初期値は、false(画像＋ラベル)です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	flag グループメニュー表示 [true:画像のみ/false:画像＋ラベル]
	 */
	public void setImageOnly( final String flag ) {
		imageOnly = nval( getRequestParameter( flag ),imageOnly );
	}

	/**
	 * 【TAG】グループメニューの表示対象となるメニューを横並びさせる数を指定します。
	 *
	 * @og.tag
	 * GROUPメニューなどの表示を行う場合に、横方向に何個のメニューを表示させるかを
	 * 指定します。例えば、画像のみのリンクと組み合わせれば、より、多くのグループを
	 * 横方向に並べることで、小領域に多くの情報を詰めることが可能になります。
	 * 0 を設定すると、横方向にのみ並べる(折り返さない)メニューを作ることが
	 * 可能になります。
	 * 初期値は、無制限です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	count 横並び数
	 */
	public void setSideCount( final String count ) {
		sideCount = nval( getRequestParameter( count ),sideCount );
	}

	/**
	 * 【TAG】表形式メニュー(MATRIX)の一つのセルに含まれる最小行数を指定します(初期値:8)。
	 *
	 * @og.tag
	 * 表形式メニュー(MATRIX)では、一つのセルの高さを同一にする為、&lt;br /&gt;タグを挿入します。
	 * このタグの挿入する個数を指定します。
	 * この個数だけ、メニューの数があろうとなかろうと行を確保します。
	 * 指定の値が、実際の行数より少ない場合は、実際の行数分だけ拡張されます。
	 * 初期値は、8 です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	count セル内最小行数
	 * @see		#setMaxCellCount( String )
	 */
	public void setMinCellCount( final String count ) {
		minCellCount = nval( getRequestParameter( count ),minCellCount );
	}

	/**
	 * 【TAG】表形式メニュー(MATRIX)の一つのセルに含まれる最大行数を指定します(初期値:8)。
	 *
	 * @og.tag
	 * 表形式メニュー(MATRIX)では、一つのセルの高さを同一にする為、指定の行数で
	 * 新たな セルを作成して、セルを横方向に連結します。
	 * 初期値は、8 です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	count セル内最大行数
	 * @see		#setMinCellCount( String )
	 */
	public void setMaxCellCount( final String count ) {
		maxCellCount = nval( getRequestParameter( count ),maxCellCount );
	}

	/**
	 * 【TAG】グループメニューのキャッシュを使用するかどうか[true/false]を指定します(初期値:true)。
	 *
	 * @og.tag
	 * GROUPメニューの表示は、一旦作成すると、ほとんど書き換えることがありません。
	 * 作成は、使用できる全メニューをスキャンして、その中からグループ属性をピックアップ
	 * するという処理を行っている為、明らかに無駄な処理です。
	 * そこで、jsp/index.jsp が実行された場合のみキャッシュをクリアして、内部では
	 * キャッシュがなければ作成し、あればキャッシュを使うロジックになっています。
	 * ここでは、キャッシュを使用するのか、毎回作成しなおすのかを指定します。
	 * 対象として、jsp/menuImage 以下に グループ名と同一の
	 * 画像ファイルが存在する場合は、画像を使用します。
	 * このフラグを、true に設定すると、画像のみを使用します。
	 * false の場合は、画像＋グループ名のラベルを使用します。
	 * 画像のみの場合でも、title 属性にグループ名のラベルをセットしますので、
	 * マウスカーソルをオーバーすれば、名称がTips表示されます。
	 * 画像が存在しない場合は、たとえ、true(画像のみ)に設定しても、ラベルを出力します。
	 * 初期値は、true(キャッシュする)です。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規登録
	 *
	 * @param	flag キャッシュ可否 [true:キャッシュする/false:キャッシュしない]
	 */
	public void setCache( final String flag ) {
		cache = nval( getRequestParameter( flag ),cache );
	}

	/**
	 * 【TAG】正判定(マッチする場合に、メニューに出す)条件を設定します。
	 *
	 * @og.tag
	 * メニューを表示する/しないの判定を、画面IDのマッチングで判断します。
	 * これは、正規表現で表される引数と、画面IDがマッチする場合に、
	 * メニューを表示させます。
	 * マッチしない場合は、表示されません。
	 * 何も指定しない場合は、ロールの判定みの行われます。
	 *
	 * @param   mt 正判定条件 (マッチする場合に、メニューに出す)
	 */
	public void setMatch( final String mt ) {
		match = nval( getRequestParameter( mt ),match );
	}

	/**
	 * 【TAG】逆判定(マッチする場合に、メニューに出さない)条件を設定します。
	 *
	 * @og.tag
	 * メニューを表示する/しないの判定を、画面IDのマッチングで判断します。
	 * これは、正規表現で表される引数と、画面IDがマッチする場合に、
	 * メニューを表示させません。
	 * マッチしない場合は、表示されます。
	 * 何も指定しない場合は、ロールの判定みの行われます。
	 *
	 * @param   umt 逆判定条件 (マッチする場合に、メニューに出さない)
	 */
	public void setUnmatch( final String umt ) {
		unmatch = nval( getRequestParameter( umt ),unmatch );
	}

	/**
	 * 【TAG】画面リンクにボタンを使用するかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * 画面リンクをボタンリンク形式で表示するかを指定します。
	 * falseの場合は、通常のリンクになります。
	 * 初期値は、false(通常リンク)です。
	 *
	 * @og.rev 4.2.1.0 (2008/04/01) 新規登録
	 *
	 * @param	flag ボタンリンク [true:ボタン形式リンク/false:通常リンク]
	 */
	public void setUseButton( final String flag ) {
		useButton = nval( getRequestParameter( flag ),useButton );
	}

//	/**
//	 * 【廃止】ボタンリンクを使用した場合に、JavaScriptのイベントを発生させるかどうか[true/false]を指定します(初期値:false)。
//	 *
//	 * @og.tag
//	 * (この属性は、廃止されています)
//	 * ボタンリンクを使用した場合に、JavaScriptのイベントを発生させるかを指定します。
//	 * 発生するイベント及び呼ばれる関数は以下の3つです。
//	 * ①クリック時 ⇒ buttonClick(this)
//	 * ②マウスオーバー時 ⇒ buttonOver(this)
//	 * ③カーソルアウト時 ⇒ buttonOut(this)
//	 * 初期値は、false(JavaScriptイベントを発生しない)です。
//	 *
//	 * @og.rev 4.2.1.0 (2008/04/01) 新規登録
//	 * @og.rev 5.1.4.0 (2010/03/01) 廃止
//	 *
//	 * @param	flag ボタンリンク [true:ボタン形式リンク/false:通常リンク]
//	 */
//	@Deprecated public void setUseButtonScript( final String flag ) {
//		useButtonScript = nval( getRequestParameter( flag ),useButtonScript );
//	}

	/**
	 * 【TAG】マトリクスからの遷移先でボタンメニュー表示するかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * マトリクスメニューからの遷移先でボタンメニューを表示させるために
	 * アドレスに付加するリクエスト変数を指定します。
	 * trueにするとbuttonRequest=trueのリクエスト変数を付けます。
	 * falseの場合はリクエスト変数を付けません。
	 * 初期値は、false(プルダウン形式で表示)です。
	 *
	 * @og.rev 4.2.1.0 (2008/04/17) 新規登録
	 *
	 * @param	flag メニュー形式 [true:ボタン形式/false:通常形式]
	 */
	public void setButtonRequest( final String flag ) {
		buttonRequest = nval( getRequestParameter( flag ),buttonRequest );
	}

	/**
	 * 【TAG】標準画面を初期状態で開いた状態で表示するかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * 通常expand=trueの状態では、メニューが折りたたまれています。
	 * このinlineStyle属性をtrueにすると標準画面のスタイルにdisplay:inlineを
	 * 付加する事で、初期状態でメニューが開いた状態になります。
	 * expand=falseとの違いは、隠しメニュー及び分類の折りたたみ機能が利用できる事です。
	 * 初期値は、false(折りたたまれた状態)です。
	 *
	 * @og.rev 4.3.3.0 (2008/10/01) 新規
	 *
	 * @param	flag メニュースタイル [true:開いた状態/false:閉じた状態]
	 */
	public void setInlineStyle( final String flag ) {
		inlineStyle = nval( getRequestParameter( flag ),inlineStyle );
	}

	/**
	 * 【TAG】ONELEVEL,NEXTGUI で、DIVタグ＋design-onelevel 処理をするかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * ONELEVEL,NEXTGUI は、QUERY 画面の上部に表示される簡易メニューです。
	 * この表記は、[画面リンク] 形式のテキスト表示されていますが、画面名称を固定長にするなどの処理を入れるため、
	 * DIVでフォーマットします。
	 * 作りは、出力される HTML を確認いただきたいと思います。
	 * 全体を、&lt;div id="design-onelevel" &gt; で、囲い、画面名称は、&lt;span clas="design-onelevel" &gt; で、囲います。
	 * これを、標準CSSで、固定幅と背景色、リンクの文字色など書き換えて、体裁を整えます。
	 * 初期値は、false(従来と同じ)です。
	 *
	 * @og.rev 5.5.2.3 (2012/05/15) 新規追加
	 *
	 * @param	flag DIVタグ処理するか [true:DIVタグ処理をする/false:しない]
	 */
	public void setUseDivOneLevel( final String flag ) {
		useDivOneLevel = nval( getRequestParameter( flag ),useDivOneLevel );
	}

	/**
	 * シリアライズ用のカスタムシリアライズ書き込みメソッド。
	 *
	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
	 * @serialData 一部のオブジェクトは、シリアライズされません。
	 *
	 * @param	strm	ObjectOutputStreamオブジェクト
	 * @throws IOException	入出力エラーが発生した場合
	 */
	private void writeObject( final ObjectOutputStream strm ) throws IOException {
		strm.defaultWriteObject();
	}

	/**
	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
	 *
	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
	 *
	 * @og.rev 4.0.0.0 (2006/09/31) 新規追加
	 * @serialData 一部のオブジェクトは、シリアライズされません。
	 *
	 * @param	strm	ObjectInputStreamオブジェクト
	 * @see #release2()
	 * @throws IOException	シリアライズに関する入出力エラーが発生した場合
	 * @throws ClassNotFoundException	クラスを見つけることができなかった場合
	 */
	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
		strm.defaultReadObject();
	}

	/**
	 * 画面オブジェクト一覧より、指定されたインデックスより後に実体となる画面ID
	 * (直リンク、通常メニュー、隠しメニュー※改行メニューは除く)が存在するかを判定します。
	 * グループスが指定されている場合は、グループで絞り込まれた結果に対して判定を行います。
	 *
	 * @og.rev 4.0.0.0 (2007/10/30) 新規追加
	 *
	 * @param guiInfos 画面オブジェクト一覧
	 * @param idx 検索を開始するインデックス番号
	 *
	 * @return 0:実体画面が存在せず 1:全て隠しの分類 2:実体画面 or 通常画面を含む分類
	 */
	private int guiCheck( final GUIInfo[] guiInfos, final int idx ) {
		int flg = 0;

		// 実態探し
		if( levelCheck( guiInfos[idx] ) > 0 ) {
			// グループメニューリンク時の処理
			if( groups == null ) { flg = 2; }
			else { flg = groupCheck( guiInfos[idx] ) ? 2 : 0; }
		}
		else {
			for( int j=idx+1; j<guiInfos.length; j++ ) {
				if( !guiInfos[j].isRead() ) { continue; }
				if( flg > 1 || guiInfos[j].getLevel() <= guiInfos[idx].getLevel() ) { break; }

				if( groups == null || groupCheck( guiInfos[j] ) ) {
					if( levelCheck( guiInfos[j] ) > 0 ) {
						flg = levelCheck( guiInfos[j] );
					}
				}
			}
		}

		return flg;
	}

	/**
	 * 指定された画面IDが実体であるか(直リンク、通常メニュー、隠しメニュー※改行メニューは除く)を判定します。
	 *
	 * @og.rev 4.0.0.0 (2007/10/30) 新規追加
	 * @og.rev 4.0.0.0 (2007/11/30) switch に default 追加
	 *
	 * @param guiInfo 画面オブジェクト
	 *
	 * @return 0:分類 1:実体画面(隠し) 2:実体画面(通常) or 直リンク
	 */
	private int levelCheck( final GUIInfo guiInfo ) {
		int flg = 0;

		switch ( guiInfo.getLevel() ) {
			case 2:
				final String adrs = guiInfo.getAddress();
				if( adrs != null && adrs.length() > 0 ) {
					flg = 2;
				}
				break;
			case 3:
				flg = "HYBS_BR".equals( guiInfo.getKey() ) ? flg : 2; break;
			case 4:
				flg = "HYBS_BR".equals( guiInfo.getKey() ) ? flg : 1; break;
			default :
				flg = 0; break;
		}

		return flg;
	}

	/**
	 * 指定された画面IDが設定されたグループスに含まれるかを判定します。
	 *
	 * @og.rev 4.0.0.0 (2007/10/30) 新規追加
	 *
	 * @param guiInfo 画面オブジェクト
	 *
	 * @return グループスに含まれるかどうか
	 */
	private boolean groupCheck( final GUIInfo guiInfo ) {
		boolean flg = false;

		for( int k=0; k<groups.length; k++ ) {
			if( guiInfo.isGroupIn( groups[k] ) ) {
				flg = true;
				break;
			}
		}

		return flg;
	}

	/**
	 * ヘルプリンク文字列を生成します。
	 *
	 * @og.rev 5.3.9.0 (2011/09/01) 新規作成
	 * @og.rev 5.0.0.4 (2012/03/16) FAQ対応
	 * @og.rev 5.5.2.5 (2012/05/21) FAQ対応 廃止
	 *
	 * @param key 画面ID
	 *
	 * @return ヘルプリンク文字列
	 * @og.rtnNotNull
	 */
	private String makeHelpLink( final String key ) {
		// 6.1.1.0 (2015/01/17) refactoring. ロジックの見直し
//		String rtn = "";
//		final String filename = helpMap.getFilename( key );
//		if( filename != null ) {
//			rtn = helpLinkFormat.replace( "{FILENAME}", filename );
//		}
//
//		return rtn;

		final String filename = helpMap.getFilename( key );
		return filename == null ? "" : helpLinkFormat.replace( "{FILENAME}", filename );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"				,VERSION			)
				.println( "menuType"			,menuType			)
				.println( "expand"				,expand				)
				.println( "groups"				,groups				)
				.println( "selClassify"			,selClassify		)
				.println( "href"				,href				)
				.println( "target"				,target				)
				.println( "imageOnly"			,imageOnly			)
				.println( "sideCount"			,sideCount			)
				.println( "minCellCount"		,minCellCount		)
				.println( "maxCellCount"		,maxCellCount		)
				.println( "cache"				,cache				)
				.println( "mscKey"				,mscKey				)
				.println( "multiSessionCheck"	,multiSessionCheck	)
				.println( "useButton"			,useButton			)
//				.println( "useButtonScript"		,useButtonScript	)
				.println( "buttonRequest"		,buttonRequest		)
				.println( "MENU_TYPE"			,MENU_TYPE			)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
