/*
 * 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.StringUtil ;
import static org.opengion.fukurou.util.StringUtil.nval ;

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：なし
 *
 * ●使用例
 *    &lt;og:topMenu /&gt;
 *
 *    &lt;og:topMenu
 *          menuType     = "NORMAL"      NORMAL:通常 / GROUP:グループメニュー / ONELEVEL:１レベル / MATRIX:一覧表メニュー / MATRIX2:一覧表メニュー(大分類なし版)
 *          expand       = "true"        true:折り返しメニュー / false:階層メニュー
 *          groups       = "AA,BB,CC"    表示対象となるグループをカンマ区切り文字列で指定します。
 *          classify     = "ABC"         表示対象となる分類を指定します。
 *          href         = "menu.jsp"    グループメニューの対象ソース名(href)を指定します。
 *          target       = "MENU"        グループメニューの対象フレーム名(target)を指定します。
 *          imageOnly    = "false"       グループメニューに、 true:画像のみ / false:画像＋ラベル を指定します。
 *          sideCount    = "1"           グループメニューの表示対象となるメニューを横並びさせる数を指定します。
 *          minCellCount = "8"           表形式メニューの１セルの最小行数を指定します。
 *          maxCellCount = "8"           表形式メニューの１セルの最大行数を指定します。
 *          cache        = "true"        グループメニューのキャッシュを使用するかどうか指定します。
 *          match        = "正規表現"    正判定(マッチする場合に、メニューに出す)条件を設定します。
 *          unmatch      = "正規表現"    逆判定(マッチする場合に、メニューに出さない)条件を設定します。
 *          useButton    = "false"       ボタン形式のリンクを使用するかを指定します。
 *          useButtonScript = "false"    ボタンのイベントによるJavaScriptを起動するかを指定します。
 *    /&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 = "4.0.0 (2005/08/31)" ;

	private static final long serialVersionUID = 4000 ;	// 4.0.0 (2005/01/31)

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

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

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

	private transient FileMap fileMap	= null;

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

//	private CodeData classifyCode = null;

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 4.0.0.0 (2007/10/31) 1レベルメニューの見直し
	 * @og.rev 4.2.1.0 (2008/04/01) マトリクスメニュー2追加
	 *
	 * @return  int
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)

		// Ver 4.0.0 2007/09/04
//		classifyCode = getResource().getCodeData( "CLASSIFY" ) ;

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

		String dir = sys( "REAL_PATH" ) + "jsp/menuImage/" ;
		fileMap = new FileMap( dir );

		if( "NORMAL".equals( menuType ) ) {
			jspPrint( "<div class=\"forpos\" id=\"dummy\"></div>" + HybsSystem.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() );
		}
		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);		// ページの残りを評価する。
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 */
	@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;				// 表示対象となるメニューを横並びさせる数を指定します。
		minCellCount		= 8;				// 表形式メニューの１セルの最小行数を指定します。
		maxCellCount		= 8;				// 表形式メニューの１セルの最大行数を指定します。
		mscKey				= null;				// MULTI_SESSION_CHECK 時のリンクに付加する情報
		fileMap				= null;				// イメージファイルのマッピングオブジェクトです。
		cache				= true;				// グループメニューのキャッシュを使用するかどうか指定します。
		match				= null;				// 正判定(マッチする場合に、メニューに出す)条件を設定
		unmatch				= null;				// 逆判定(マッチする場合に、メニューに出さない)条件を設定
		useButton			= false;			// ボタン形式のリンクを使用するか
		useButtonScript		= false;			// ボタンのイベントによるJavaScriptを起動するか
		buttonRequest		= false;			// trueでボタンメニューの表示を行う
		excludeButton		= false;			// trueでボタンメニュー時でも強制的に従来のプルダウンにする。
		inlineStyle			= false;			// trueの場合は標準画面(Lv3)にdisplay:inlineを付加
	}

	/**
	 * メニューを表示する為の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 (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タグで囲う。
	 *
	 * @return  メニュー
	 */
	private String makeMenu() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_LARGE );

		UserInfo userInfo = getUser();

		// 4.0.0 (2005/01/31)

//		boolean useLvl3 = false;		// レベル３が存在すると肯定(true)
//		boolean allLvl3 = true;			// レベル２が存在すると否定(false) つまり、true は全件レベル３
//		boolean useFldLvl2 = false;		// fieldset が初期化されるまでに、レベル２が存在すると肯定(true)
//		boolean useClsEllipses = false;
//		int		fldInAdrs	= -1;		// fieldsetタグ(lebel=0)の挿入する位置
//		String classify		;			// 分類コード
//		String bkClassify	= null;		// 分類コード(旧)
//		String fieldData	= null;		// fieldset を挿入する場合の文字列。

		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; // 分類隠しが存在するか？
		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) に含まれない
					}
				}

				// 処理すべき画面かのチェック
				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 ) {
						rtn.append( makeEllipses( menuNo,kmokuNo,3 ) );
						kmokuNo++;
					}
					isExistHiddenMenu = false;
				}

				// 大分類(フィールドメニュー)
				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(),gamenId,level ) );

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

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

						// 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( ">");
						
					}
					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(),gamenId,level ) );
					rtn.append( HybsSystem.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;
							}

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

						// 画面IDが"HYBS_BR"の時は処理しない
						if( !"HYBS_BR".equals( guiInfos[i].getKey() ) ) {
							rtn.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),gamenId,level ) );
						}
						rtn.append( HybsSystem.BR );
						if( expand ) {
							rtn.append( "</div>" );
						}
						kmokuNo++;
					}
				}

				rtn.append( HybsSystem.CR );

				preLevel = level;
			}
		}

		// 終了処理
		if( isExistHiddenMenu ) {
			rtn.append( makeEllipses( menuNo,kmokuNo,3 ) );
		}

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

		if( isExistClassifyHidden ) {
			rtn.append( makeEllipses( menuNo,0,2 ) );
		}

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

//				// CLASSIFY に HYBS_BR が設定されていると1行空行を挿入します。
//				if( "HYBS_BR".equals( classify ) ) {
//					if( expand ) {
//						rtn.append( "<div class=\"expand2\" id=\"menu" );
//						rtn.append( menuNo ).append( "_" ).append( kmokuNo ).append( "\">" ) ;
//					}
//					rtn.append( HybsSystem.BR );
//					if( expand ) {
//						rtn.append( "</div>" ).append( HybsSystem.CR );
//					}
//					kmokuNo++;
//					continue;
//				}
//
//				// 分類(CLASSIFY) のブレイク時処理
//				if( !classify.equals( bkClassify ) && level >= 2 ) {
//					bkClassify = classify;
//					// 先の分類中にレベル３が存在すれば、"←･･･→" を出力
//					if( expand && useLvl3 ) {
//						rtn.append( makeEllipses( menuNo,kmokuNo,2 ) );
//
//						// すべてが レベル３の場合、expand1 を見つけて、unpopular を挿入する。
//						if( allLvl3 ) {
//							int expInAdrs = rtn.lastIndexOf( "expand1" );
//							if( expInAdrs >= 0 ) {
//								expInAdrs += "expand1".length();
//								rtn.insert( expInAdrs, " unpopular" );
//								useClsEllipses = true;		// 分類メニューに、unpopular を使用した場合。
//
//								// レベル０(fieldset)の挿入位置が unpopular 挿入分だけずれる。
//								if( fldInAdrs > expInAdrs ) {
//									fldInAdrs += " unpopular".length();
//								}
//							}
//						}
//					}
//					useLvl3 = false;	// 初期化：レベル３が存在すると肯定(true)
//					allLvl3 = true;		// 初期化：レベル２が存在すると否定(false)
//
//					menuNo++;
//					kmokuNo = 0;
//
////					String classifyLabel = getResource().getLabelData( classify ).getShortLabel() ;
//					int cdAdrs = classifyCode.getAddress( classify ) ;
////					String classifyLabel = classifyCode.getShortLabel( cdAdrs );
//					String classifyLabel;
//					if( cdAdrs != -1 ) {
//						classifyLabel = classifyCode.getShortLabel( cdAdrs );
//					}
//					else {
//						classifyLabel = classify;
//					}
//
//					if( expand ) {
//						rtn.append( "<div class=\"expand1" );
//						rtn.append( "\" id=\"menu" );
//						rtn.append( menuNo ).append( "\">" );
//					}
//					rtn.append( makeTagMenuString( null,null,classifyLabel,classify,1 ) );
//					rtn.append( HybsSystem.BR );
//					if( expand ) {
//						rtn.append( "</div>" ).append( HybsSystem.CR );
//					}
//				}
//
//				String linkLabel = guiInfos[i].getName();
//				if( level == 0 ) {
//					// level=1 は、大項目の出力なので、初期化が必要になる。
//					// 先の分類中にレベル３が存在すれば、"←･･･→" を出力
//					if( expand && useLvl3 ) {
//						rtn.append( makeEllipses( menuNo,kmokuNo,2 ) );
//					}
//					useLvl3 = false;	// 初期化：レベル３が存在すると肯定(true)
//					allLvl3 = true;		// 初期化：レベル２が存在すると否定(false)
//
//					if( useFldLvl2 && fieldData != null ) {
//						rtn.insert( fldInAdrs, fieldData );
//						rtn.append( FIELD_OUT );
//					}
//					fieldData = makeTagMenuString( null,null,linkLabel,gamenId,level );
//					fldInAdrs = rtn.length();
//					useFldLvl2 = false;		// 初期化：レベル２が存在すると肯定(true)
//				}
//				else if( level == 1 ) {
//					useFldLvl2 = true;	// レベル２が存在すると肯定(true)
//
//					// level=1 は、大項目の出力なので、初期化が必要になる。
//					// 先の分類中にレベル３が存在すれば、"←･･･→" を出力
//					if( expand && useLvl3 ) {
//						rtn.append( makeEllipses( menuNo,kmokuNo,2 ) );
//					}
//					useLvl3 = false;	// 初期化：レベル３が存在すると肯定(true)
//					allLvl3 = true;		// 初期化：レベル２が存在すると否定(false)
//
//					rtn.append( makeTagMenu( guiInfos[i],linkLabel,gamenId,level ) );
//					rtn.append( HybsSystem.BR );
//				}
//				else {
//					if( expand ) {
//						if( level == 3 ) {
//							useLvl3 = true;
//							rtn.append( "<div class=\"expand2 unpopular\" id=\"menu" );
//						}
//						else {
//							useFldLvl2 = true;	// レベル２が存在すると肯定(true)
//							allLvl3    = false;
//							rtn.append( "<div class=\"expand2\" id=\"menu" );
//						}
//						rtn.append( menuNo ).append( "_" ).append( kmokuNo ).append( "\">" ) ;
//					}
//					rtn.append( makeTagMenu( guiInfos[i],linkLabel,gamenId,level ) );
//					rtn.append( HybsSystem.BR );
//					if( expand ) {
//						rtn.append( "</div>" ).append( HybsSystem.CR );
//					}
//				}
//				kmokuNo++;
//			}
//		}
//
//		// ループ終了時の後処理。
//		// 先の分類中にレベル３が存在すれば、"←･･･→" を出力
//		if( expand && useLvl3 ) {
//			rtn.append( makeEllipses( menuNo,kmokuNo,2 ) );
//		}
//
//		// useFldLvl2 が true で、fieldData が存在する場合
//		if( useFldLvl2 && fieldData != null ) {
//			rtn.insert( fldInAdrs, fieldData );
//			rtn.append( FIELD_OUT );
//		}
//
//		// 分類中のすべてがレベル３の場合に、分類を隠した場合の、"←･･･→" を出力
//		if( expand && useClsEllipses ) {
//			rtn.append( makeEllipses( ++menuNo,-1,1 ) );
//		}

		return rtn.toString();
	}

	/**
	 * メニューを表示する為のHTMLを作成します(折り返しJavaScript対応版)。
	 *
	 * @og.rev 4.0.0 (2005/01/31) 個人別のお気に入りメニューを作成します。
	 * @og.rev 4.0.0.0 (2007/10/31) 分類項目の廃止
	 * @og.rev 4.1.1.0 (2008/02/05) お気に入り情報はGEA09から取得するように変更
	 *
	 * @return  個人別のお気に入りメニュー
	 */
	private String makeFavoriteMenu() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_LARGE );

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

//		if( favoriteMap.size() == 0 ) { return ""; }
		if( favoriteMap.isEmpty() ) { return ""; }

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

		// fieldSetタグ、お気に入り編集画面リンクの出力
		GUIInfo editFavorite = userInfo.getGUIInfo( "GE0014" );
		rtn.append( "<div class=\"expand1\"> " );
		rtn.append( makeTagMenuString( null,null,largeClassify,null,1 ) );
		rtn.append( makeTagMenu( editFavorite,resource.getLabelData( "EDIT" ).getShortLabel(),"GE0014",2 ) );
		rtn.append( HybsSystem.BR );

		String thisClassify = null;
		String gamenId = null;
		FavoriteGUIData favoriteGui = null;
		// 4.3.4.4 (2009/01/01) findBugs警告対応
//		for( String gamenId : favoriteMap.keySet() ) {
//			favoriteGui = favoriteMap.get( gamenId );
		for( Map.Entry<String, FavoriteGUIData> entry : favoriteMap.entrySet() ) {
			gamenId = entry.getKey();
			favoriteGui = entry.getValue();
			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" );
					rtn.append( menuNo ).append( "\">" );
				}
				rtn.append( makeTagMenuString( null,null,thisClassify,thisClassify,2 ) );
				rtn.append( HybsSystem.BR );
				if( expand ) {
					rtn.append( "</div>" ).append( HybsSystem.CR );
				}
				lastClassify = thisClassify;
			}

			if( expand ) {
				rtn.append( "<div class=\"expand2\" id=\"menu" );
				rtn.append( menuNo ).append( "_" ).append( kmokuNo ).append( "\">" ) ;
			}

			String linkLabel = favoriteGui.getName();
			rtn.append( makeTagMenu( guiInfo,linkLabel,gamenId,3 ) );
			rtn.append( HybsSystem.BR );
			if( expand ) {
				rtn.append( "</div>" ).append( HybsSystem.CR );
			}
			kmokuNo++;
		}
		rtn.append( FIELD_OUT );
		rtn.append( "</div>" );

		return rtn.toString();
	}

	/**
	 * 折りたたみメニューで、非標準メニューの 表示に使う、"←･･･→" を作成します。
	 *
	 * @param  menuNo int 階層番号
	 * @param  kmokuNo int 階層項目番号
	 * @param  type int タイプ(1,2限定)
	 *
	 * @og.rev 4.0.0 (2005/01/31) 新規作成
	 *
	 * @return  メニュー
	 */
	private String makeEllipses( final int menuNo,final int kmokuNo,final int type ) {
		int tmpType = type-1; // 超暫定対応 4.0.0.0 (2007/10/31)
		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 )
		//			+ HybsSystem.BR + "</div>" + HybsSystem.CR ;
		String rtn = "<div class=\"expand" + tmpType + " ellipses\" id=\"menu" + menuNo
		+ kmkNo  + "\" ";
		if( inlineStyle ){
			rtn += "style=\"display:inline\" ";
		}
		rtn += ">" + makeTagMenuString( null,null,"←･･･→",null,type )
			+ HybsSystem.BR + "</div>" + HybsSystem.CR ;

		return rtn ;
	}

	/**
	 * menuType="GROUP" 時に作成するグループメニュー
	 *
	 * @og.rev 4.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)の表示文字にラベルリソースを使う。
	 *
	 * @return  メニュー
	 */
	private String makeGroupMenu() {

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

		UserInfo userInfo = getUser();

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

		// そのユーザーで使用できる画面をすべてピックアップします。
		// その上で、読取可能なメニューを含むグループを順番に Set にセットしていきます。
		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; }
				String[] groups = StringUtil.csv2Array( guiInfos[i].getGroups() );
				for( int j=0; j<groups.length; j++ ) {
					groupSet.add( groups[j] );
				}
			}
		}

		if( groupSet.size() > 1 ) {
			int sideTmpCnt = 1;
			StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_LARGE );
			rtn.append( "<tr>" );

//			rtn.append( makeTagMenuString( href,target,"ALL","GUI_ALL",-1 ) );
			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++ ;

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

					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 ) {
			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 (2005/01/31) GUIInfoの実アドレスのパラメータを考慮する。
	 * @og.rev 4.0.0.0 (2007/10/31) 一旦廃止
	 *
	 * @return  メニュー
	 */
	private String makeOneLevelMenu() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_LARGE );

		UserInfo userInfo = getUser();

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

		boolean isInClassify = false;

		for( int i=0; i<guiInfos.length; i++ ) {
//			classify = guiInfos[i].getClassify();	// 分類キー
			int level = guiInfos[i].getLevel();

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

//			if( guiInfos[i].isRead() && selClassify.equals( classify ) ) {		// 4.0.0 (2005/01/31)
			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; }

//				int level = guiInfos[i].getLevel();		// 4.0.0 (2005/01/31)
//				if( level == 2 ) {
//				if( level == 4 ) {
				String guiLabel = guiInfos[i].getName();		// 画面ラベル

				String thisGamenId = getGUIInfoAttri( "KEY" );
				if( gamenId.equals( thisGamenId ) ) {
					rtn.append( "[" ).append( guiLabel ).append( "] " );
				}
				else {
					rtn.append( makeTagMenu( guiInfos[i],guiLabel,gamenId,-2 ) );
				}

				if( sideCount > 0 ) {
					// 全件数をサイドカウント数で割った値が最大行数の場合は、終了する。
					if( sideTmpCnt / sideCount >= maxCellCount ) {
						//						rtn.append("･･･");
						break;
					}
					// 全件数をサイドカウント数で割った余りがゼロの場合は、行ブレイクする。
					if( sideTmpCnt % sideCount == 0 ) { rtn.append( HybsSystem.BR ); }
					sideTmpCnt++ ;
				}
//				}
			}
		}

		return rtn.toString();
	}

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

		UserInfo userInfo = getUser();

		// 4.0.0 (2005/01/31)
//		String classify		;			// 分類コード
//		int	   colspanAdrs	= 0;		// maxCellCount でのキーブレイクで、colspan を挿入する位置
//		int	   colspan		= 0;		// maxCellCount でのキーブレイクで、colspan を挿入する値

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

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

		int level ;
		int preLevel = 0;

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

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

		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; }

				// 処理すべき画面かのチェック
				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( HybsSystem.BR ); }
						rtnB.append( "</td>" ).append( HybsSystem.CR );
					}

					if( bkClassifyKey != null ) {
						rtnH.append( "<th colspan=\"" + cellTmpCnt + "\" class=\"MENU_H CLR" + cellColorCnt + "\">" );
						if( "_SPACE".equals( bkClassifyKey ) ) {
							rtnH.append( "　" );
						}
						else {
							rtnH.append( makeTagMenuString( null,null,bkClassifyName,bkClassifyKey,-3 ) );
						}
						rtnH.append( "</th>" );
					}

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

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

					}

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

					rtnH = new StringBuilder( HybsSystem.BUFFER_LARGE );
					rtnB = new StringBuilder( HybsSystem.BUFFER_LARGE );
				}
				// 分類(直リンクの場合は、level=3で処理)
				else if( level == 2 ) {
					isInClassify = true;
					bkClassifyKey = guiInfos[i].getKey();
					bkClassifyName = guiInfos[i].getName();
				}
				// 通常メニュー・隠しメニュー
				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( HybsSystem.BR ); }
							lineTmpCnt = 0;
						}
					}
					// 通常画面
					else {
						if( lineTmpCnt == 0 ) {
							rtnB.append( "<td class=\"MENU_B\">" );
							cellTmpCnt++;
						}

						rtnB.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),gamenId,-3 ) );
						lineTmpCnt++;

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

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

				preLevel = level;
			}

		}

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

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

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

//				classify = guiInfos[i].getClassify();	// 分類キー
//				if( selClassify != null && !selClassify.equals( classify ) ) {
//					continue;		// 分類(classify) に含まれない
//				}
//
//				String linkLabel = guiInfos[i].getName();

//				if( level == 1 ) {
//					if( "HYBS_BR".equals( guiInfos[i].getKey() ) ) {	// レベル ０ の HYBS_BR は 色チェンジ
//						cellColorCnt++ ;
//						continue;
//					}
//
//					if( rtnB != null ) {
//						// セルに含まれる最小行数処理
//						for( int j=lineTmpCnt; j<minCellCount; j++ ) { rtnB.append( HybsSystem.BR ); }
//						rtnB.append( "</td>" );
//
//						rtn.append( rtnH ).append( "</tr>" ).append( HybsSystem.CR ).append( "<tr>" );
//						rtn.append( rtnB ).append( "</tr>" ).append( HybsSystem.CR );
//						cellColorCnt++ ;
//					}
//
//					rtnH = new StringBuilder();
//					rtnH.append( "<tr><td rowspan=\"2\" class=\"MENU_S\">" );
//					rtnH.append( makeTagMenuString( null,null,guiInfos[i].getName(),gamenId,-3 ) );
//					rtnH.append( "</td>" ).append( HybsSystem.CR );
//					rtnB = null;
//					lineTmpCnt = 0;
//				}
//				else {
//					// 分類(CLASSIFY) のブレイク時処理
//					boolean hybsBR = "HYBS_BR".equals( classify );		// 最優先
//					boolean clsBrk = !hybsBR && !classify.equals( bkClassify );	// 次優先
//					boolean cntBrk = !clsBrk && ( lineTmpCnt > 0 && lineTmpCnt % maxCellCount == 0 ) ;
//
//					if( rtnH == null ) { continue; }	// レベル0 が必ず先頭である。
//
//					if( clsBrk || cntBrk || hybsBR || rtnB == null ) {
//						if( cntBrk || hybsBR ) {
//							colspan++;
//							rtnH = rtnH.replace( colspanAdrs,colspanAdrs+1,String.valueOf( colspan ) );
//						}
//						else {
//							bkClassify = classify;
//							colspan = 1;
////							String classifyLabel = getResource().getLabelData( classify ).getShortLabel() ;
////							int cdAdrs = classifyCode.getAddress( classify ) ;
//							// 4.0.0.0 (2007/10/05) 分類のコードリソースが存在しない場合
////							String classifyLabel = classifyCode.getShortLabel( cdAdrs );
//							String classifyLabel = "";
////							if( cdAdrs >= 0 ) { classifyLabel = classifyCode.getShortLabel( cdAdrs ); }
//
//							rtnH.append( "<th colspan=\"1\"" );
//							colspanAdrs = rtnH.length() - 2;	// 文字数からダブルクオートを考慮して 1 の位置を求める。
//							rtnH.append( " class=\"MENU_H CLR" ).append( cellColorCnt ).append( "\">" );
//							rtnH.append( makeTagMenuString( null,null,classifyLabel,classify,-3 ) );
//							rtnH.append( "</th>" );
//						}
//
//						if( rtnB == null ) { rtnB = new StringBuilder(); }
//						else {
//							// セルに含まれる最小行数処理
//							for( int j=lineTmpCnt; j<minCellCount; j++ ) { rtnB.append( HybsSystem.BR ); }
//							rtnB.append( "</td>" );
//						}
//						lineTmpCnt = 0;
//						rtnB.append( "<td class=\"MENU_B\">" );
//					}
//					if( !hybsBR ) {
//						lineTmpCnt++ ;
//						rtnB.append( makeTagMenu( guiInfos[i],linkLabel,gamenId,-3 ) );
//						rtnB.append( HybsSystem.BR );
//					}
//				}
//			}
//		}
//
//		if( rtnB != null ) {
//			rtn.append( rtnH ).append( "</tr>" ).append( HybsSystem.CR ).append( "<tr>" );
//			// セルに含まれる最小行数処理
//			for( int j=lineTmpCnt; j<minCellCount; j++ ) { rtnB.append( HybsSystem.BR ); }
//			rtn.append( rtnB ).append( "</td></tr>" ).append( HybsSystem.CR );
//		}

		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()に変更
	 *
	 * @return  マトリクスメニュー
	 */
	private String makeMatrixMenu2() {
		StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_LARGE );

		UserInfo userInfo = getUser();

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

		StringBuilder rtnH = new StringBuilder( HybsSystem.BUFFER_LARGE );		// 分類部分の出力用
		StringBuilder rtnB = new StringBuilder( HybsSystem.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;		// 小分類のリンク

		rtn.append( "<tr>" ).append( HybsSystem.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; }

				// 処理すべき画面かのチェック
				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( HybsSystem.BR ); }
						//rtnB.append( "</td>" ).append( HybsSystem.CR ); // 4.2.1.0 (2008/04/03) リスト
						rtnB.append( "</ul></div></td>" ).append( HybsSystem.CR );
					}

					if( bkClassifyKey != null ) {
						rtnH.append( "<th colspan=\"" + cellTmpCnt + "\" class=\"MENU_H CLR" + cellColorCnt + "\">" );
						if( "_SPACE".equals( bkClassifyKey ) ) {
							rtnH.append( "　" );
						}
						else {
							classifyHref = JSP + "/index.jsp?classify=" + bkClassifyKey; 
//	4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
//							if( buttonRequest ){ // 4.2.1.0 (2008/04/17) マトリクス２からの遷移でボタン式にするかどうか
//								classifyHref += "&amp;buttonRequest=true";
//							}
							classifyHref += "&amp;GAMENID="+nextKey; // ボタンを押した場合に最初の画面が現れる
							rtnH.append( makeTagMenuString( classifyHref,"_self",bkClassifyName,bkClassifyKey,-3 ) );
						}
						rtnH.append( "</th>" );
						
						cellTmpAllCnt += cellTmpCnt;
						
						if( cellTmpAllCnt >= sideCount ) {
							rtn.append( rtnH ).append( "</tr><tr>" ).append( rtnB ).append( "</tr>" );

							rtnH = new StringBuilder( HybsSystem.BUFFER_LARGE );
							rtnB = new StringBuilder( HybsSystem.BUFFER_LARGE );
							cellTmpAllCnt = 0;
						}
					}

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

				// 分類(直リンクの場合は、level=3で処理)
				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();
//					if( guiInfos.length > i && guiInfos[i+1] != null ){
//						nextKey = guiInfos[i+1].getKey();
//					}
					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( HybsSystem.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\"> <div class=\"gamen-menu-wrap\"><ul class=\"gamen-menu\">" ); 
							cellTmpCnt++;
						}
						
						rtnB.append( "<li>" ); // 4.2.1.0 (2008/04/03) リスト
						rtnB.append( makeTagMenu( guiInfos[i],guiInfos[i].getName(),gamenId,-4 ) );
						rtnB.append( "</li>" ); // 4.2.1.0 (2008/04/03) リスト
						lineTmpCnt++;

						if( lineTmpCnt >= maxCellCount ) {
							// rtnB.append( "</td>" );
							rtnB.append( " </ul> </div></td>" ); // 4.2.1.0 (008/04/03) リスト
							lineTmpCnt = 0;
						}
				//		else {
				//			rtnB.append( HybsSystem.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( HybsSystem.BR ); }
			rtnB.append( "</td>" ).append( HybsSystem.CR );
		}

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

			classifyHref = JSP + "/index.jsp?classify=" + bkClassifyKey;
//	4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
//			if( buttonRequest ){ // 4.2.1.0 (2008/04/17) マトリクス２からの遷移でボタン式にするかどうか
//				classifyHref += "&amp;buttonRequest=true";
//			}
			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:マトリクスメニュー(一覧)
	 *
	 * @og.rev 4.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対応
	 *
	 * @param  guiInfo GUIInfo リンク
	 * @param  body String BODY部(表示)
	 * @param  imgKey String imageファイルの検索キー
	 * @param  type int タイプ
	 * @return  階層別メニュー文字列
	 */
	private String makeTagMenu( final GUIInfo guiInfo, final String bodyIn,
								final String imgKey,final int type ) {

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

			// 4.3.3.7 (2008/11/22) https対応
//			if( ! "http".equals( guiInfo.getKblink() ) ) {
//			if( ! readAdrs.startsWith( "http://" ) ) {
			if( ! readAdrs.startsWith( "http://" ) && ! readAdrs.startsWith( "https://" )) {
				if( type == -3 ) {
					tmpTarget = target;
					String param = getRequestParameter( guiInfo.getParam() );
					href = XHTMLTag.addUrlEncode( JSP + "/result.jsp",param );
//	 4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
//					if( buttonRequest ){ // 4.2.1.0 (2008/04/17) マトリクスからの遷移でボタン式にするかどうか
//						href = XHTMLTag.addUrlEncode( href,"buttonRequest=" + buttonRequest );
//					}
				}
				else if( type == -4 ) { // 4.2.1.0 (2008/04/01)
					tmpTarget = "_self";
					String param = getRequestParameter( guiInfo.getParam() );
					href = XHTMLTag.addUrlEncode( JSP + "/index.jsp",param );
					href = XHTMLTag.addUrlEncode( href,"classify=" + guiInfo.getClassify() );
//	4.1.2.0(2008/05/14) リクエスト変数を付ける位置変更
//					if( buttonRequest ){ // 4.2.1.0 (2008/04/17) マトリクス２からの遷移でボタン式にするかどうか
//						href = XHTMLTag.addUrlEncode( href,"buttonRequest=" + buttonRequest );
//					}
				}
				href = XHTMLTag.addUrlEncode( href,"GAMENID=" + guiInfo.getKey() );

				// 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 );
				}
			}
		}
		return makeTagMenuString( href,tmpTarget,bodyIn,imgKey,type ) ;
	}

	/**
	 * タイプに応じたメニューを表示する為の文字列を作成します。
	 *
	 * 　0:フィールドセットメニュー
	 * 　1:トップ階層(【分類名称】)
	 * 　2:選択階層(通常の折りたたみメニュー)
	 * 　3:選択非表示(通常は、隠してあります)
	 * 　-1:グループ分類メニュー(class="GUI_GRP"のtdタグで囲われます)
	 * 　-2:ラインメニュー([画面名称]　)
	 * 　-3:マトリクスメニュー(一覧)
	 * 　-4:マトリクスメニュー2(一覧)
	 *
	 * @og.rev 4.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 で重複項目をまとめます。
	 *
	 * @param  href String リンク
	 * @param  target String ターゲット
	 * @param  body String BODY部(表示)
	 * @param  imgKey String imageファイルの検索キー
	 * @param  type int タイプ
	 * @return  階層別メニュー文字列
	 */
	private String makeTagMenuString( final String href,final String target, final String bodyIn,
								final String imgKey,final int type ) {

		final String body ;
		String imgFile = fileMap.getFilename( imgKey );
		if( imgFile != null ) {
			if( imageOnly ) {
				body = "<img src=\"" + JSP + "/menuImage/" + imgFile + "\" title=\"" + bodyIn + "\" />" ;
			}
			else {
				body = "<img src=\"" + JSP + "/menuImage/" + imgFile + "\"/>" + bodyIn ;
			}
		}
		else {
			if( imageOnly && bodyIn != null ) {
				body = "<span title=\"" + bodyIn + "\" >" + 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)
				tag = new TagBuffer( "button" );
				String onClick = "top."+target+".location.href=\'"+ href + "\';";
				if( useButtonScript ) {
					onClick = onClick + "buttonClick(this);";
					tag.add( "onmouseover" , "buttonOver(this);" );
					tag.add( "onmouseout" , "buttonOut(this);" );
				}
				tag.add( "onclick" , onClick );
				tag.add( "class" , "buttonMenu" ); 	// 4.2.1.0 (2008/04/03) classを追加
				tag.add( "id" , imgKey );			// 4.2.1.0 (2008/04/11) idをimgKey(実質画面ID)で追加
				tag.setBody( body );
			}
			else {
				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( "href"		, href2 );
				tag.add( "target"	, target );
				tag.setBody( body );
			}
			in = tag.makeTag();
		}
		else {
			in = body;
		}

		final String rtn ;
		switch( type ) {
			case 1: rtn = FIELD_IN + "<legend>" + in + "</legend>" ; break;	// 大分類
			case 2: rtn = "【"	+ in + "】" ; break;	// 小分類・直リンク
//			case 3: rtn = "　"	+ in		; break;	// 通常メニュー
			case 3: 									// 通常メニュー
			case 4: rtn = "　"	+ in 		; break;	// 隠れメニュー
			case -1: rtn = "<td class=\"MENU_G " + imgKey + "\">" + in + "</td>" ; break;	// グループメニュー
			case -2: rtn = "["	+ in + "] "; break;		// ラインメニュー
			// 4.3.5.0 (2009/02/01) switch の case で重複項目をまとめます。
//			case -3: rtn =  in		; break;			// マトリクスメニュー
			//case -4: rtn = "■ " + in		; break;
			case -3:									// マトリクスメニュー
			case -4: rtn =  in		; break; // 4.2.1.0 (2008/04/03) リスト。リストのため■はいらない
			default :
				rtn = "X_" + in ;
		}

		return rtn ;
	}

	/**
	 * 【TAG】作成するメニューの種類(NORMAL,GROUP,ONELEVEL,MATRIX,MATRIX2)を指定します(初期値:NORMAL)。
	 *
	 * @og.tag
	 * 作成するメニューには、複数の種類があります。
	 * <table>
	 * <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>MATRIX  </td><td>一覧表形式のメニューを作成します。</td></tr>
	 * </table>
	 *
	 * @og.rev 4.0.0 (2005/01/31) 新規登録
	 *
	 * @param	type メニューの種類(NORMAL,GROUP,ONELEVEL,MATRIX,MATRIX2)
	 */
	public void setMenuType( final String type ) {
		menuType = nval( getRequestParameter( type ),MENU_TYPE[0] );
		if( ! check( menuType, MENU_TYPE ) ) {
			String errMsg = "menuType に、指定できない種類の文字が指定されました。menuType=["
						+ menuType + "]" + HybsSystem.CR
						+ "以下の中から、選択してください。["
						+ StringUtil.array2csv( MENU_TYPE ) + "]" + HybsSystem.CR;
			throw new HybsSystemException( errMsg );
		}
	}

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

	/**
	 * 【TAG】表示対象となるグループをカンマ区切り文字列で指定します。
	 *
	 * @og.tag
	 * メニューの表示対象グループをカンマ区切り文字列で複数指定できます。
	 * 指定のグループのメニューだけが、表示対象になります。
	 * メニューにも、複数のグループを指定できるため、１グループの指定で、
	 * 複数のくくりを表示することも可能です。
	 * グループを指定しない場合は、全グループが対象になります。
	 * また、メニュー側にグループ指定がない場合は、グループ指定に
	 * 関係なく、対象になります。
	 * 初期値は、未指定(全メニューが対象)です。
	 * 分解方法は、通常のパラメータ取得後に、CSV分解します。
	 *
	 * @og.rev 4.0.0 (2005/01/31) 新規登録
	 *
	 * @param	menuGroups 表示対象となるグループ(カンマ区切り文字列)
	 */
	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 (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 (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 (2005/01/31) 新規登録
	 *
	 * @param	inTarget 表示対象となるフレーム名(target)
	 */
	public void setTarget( final String inTarget ) {
		target = nval( getRequestParameter( inTarget ),target );
	}

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

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

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

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

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

	/**
	 * 【TAG】画面リンクにボタンを使用するかを指定します。(初期値: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 );
	}
	
	/**
	 * 【TAG】ボタンリンクを使用した場合に、JavaScriptのイベントを発生させるかを指定します。(初期値:false)。
	 *
	 * @og.tag
	 * ボタンリンクを使用した場合に、JavaScriptのイベントを発生させるかを指定します。
	 * 発生するイベント及び呼ばれる関数は以下の3つです。
	 * ①クリック時 -> buttonClick(this)
	 * ②マウスオーバー時 -> buttonOver(this)
	 * ③カーソルアウト時 -> buttonOut(this)
	 * 初期値は、false(JavaScriptイベントを発生しない)です。
	 *
	 * @og.rev 4.2.1.0 (2008/04/01) 新規登録
	 *
	 * @param	flag true:ボタン形式リンク / false:通常リンク
	 */
	public void setUseButtonScript( final String flag ) {
		useButtonScript = nval( getRequestParameter( flag ),useButtonScript );
	}
	
	/**
	 * 【TAG】マトリクスからの遷移先でボタンメニュー表示するのか(初期値: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】標準画面を初期状態で表示するか(初期値: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 );
	}

	/**
	 * シリアライズ用のカスタムシリアライズ書き込みメソッド
	 *
	 * @og.rev 4.0.0 (2006/09/31) 新規追加
	 * @serialData
	 *
	 * @param strm ObjectOutputStream
	 */
	private void writeObject( final ObjectOutputStream strm ) throws IOException {
		strm.defaultWriteObject();
	}

	/**
	 * シリアライズ用のカスタムシリアライズ読み込みメソッド
	 *
	 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
	 *
	 * @og.rev 4.0.0 (2006/09/31) 新規追加
	 * @serialData
	 *
	 * @param strm ObjectInputStream
	 * @see #release2()
	 */
	private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
		strm.defaultReadObject();
	}

	/**
	 * 画面オブジェクト一覧より、指定されたインデックスより後に実体となる画面ID
	 * (直リンク、通常メニュー、隠しメニュー※改行メニューは除く)が存在するかを判定します。
	 * グループスが指定されている場合は、グループで絞り込まれた結果に対して判定を行います。
	 *
	 * @og.rev 4.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 (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:
//				if( guiInfo.getAddress() != null && guiInfo.getAddress().length() > 0 ) {
				String adrs = guiInfo.getAddress();
				if( adrs != null && adrs.length() > 0 ) {
					flg = 2;
				}
				break;
			case 3:
//				flg = !"HYBS_BR".equals( guiInfo.getKey() ) ? 2 : flg; break;
				flg = "HYBS_BR".equals( guiInfo.getKey() ) ? flg : 2; break;
			case 4:
//				flg = !"HYBS_BR".equals( guiInfo.getKey() ) ? 1 : flg; break;
				flg = "HYBS_BR".equals( guiInfo.getKey() ) ? flg : 1; break;
			default :
				flg = 0; break;
		}

		return flg;
	}

	/**
	 * 指定された画面IDが設定されたグループスに含まれるかを判定します。
	 *
	 * @og.rev 4.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;
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 */
	public String toString() {
		return org.opengion.fukurou.util.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() ;
	}
}
