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

import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.HybsEntry;
import static org.opengion.fukurou.system.HybsConst.CR ;				// 6.1.0.0 (2014/12/26)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;		// 6.1.0.0 (2014/12/26) refactoring

import java.util.List;
import java.util.ArrayList;
import java.util.Deque;											// 6.8.4.2 (2017/12/25)
import java.util.concurrent.ConcurrentLinkedDeque;				// 6.8.4.2 (2017/12/25)
import java.util.stream.Collectors;								// 6.4.3.4 (2016/03/11)

/**
 * 画面情報の取得の為のインターフェースです。
 *
 * {&#064;GUI.XXXX} で、XXXX 部に、GUIInfo オブジェクトで定義されている
 * 属性情報を取り出すことが出来ます。
 *
 * ・KEY           画面ID
 * ・ADDRESS       実行アドレス
 * ・REALADDRESS   実行実アドレス
 * ・SEQUENCE      表示順
 * ・GROUPS        メニュグループ
 * ・CLASSIFY      メニュ分類
 * ・LEVEL         メニュ階層番号
 * ・LABEL         画面名称
 * ・NAME          画面名称(=SNAME)
 * ・SNAME         画面名称(short)
 * ・LNAME         画面名称(long)
 * ・ROLES         ロールズ
 * ・MODE          アクセスモード列(mr,mw,-r,-w の羅列)
 * ・TARGET        ターゲット
 * ・PARAM         設定値(パラメータ)
 * ・KBLINK        リンク区分
 * ・DESCRIPTION   概要説明
 * ・IMAGEKEY      イメージキー
 * ・DYUPD         更新日時
 * ・ISREAD        読取り許可[true/false]
 * ・ISWRITE       書込み許可[true/false]
 *
 * @og.group リソース管理
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class GUIInfo implements Comparable<GUIInfo> {	// 4.3.3.6 (2008/11/15) Generics警告対応

	private static final String YOYAKU = "|KEY|ADDRESS|REALADDRESS|SEQUENCE"
										+ "|GROUPS|CLASSIFY|LEVEL|LABEL|NAME"
										+ "|SNAME|LNAME|ROLES|MODE|TARGET"
										+ "|PARAM|KBLINK|DESCRIPTION|DYUPD|IMAGEKEY|" ;		// 5.5.2.5 (2012/05/21) イメージアイコン

	private final GUIData	guiData		;
	private final LabelData	labelData	;
	private final String[]	groupKeys	;

	private final boolean	menuFlag	;		// メニューへの表示可否属性
	private final boolean	writeFlag	;		// 書き込み許可属性
	private final byte		bitMode		;		// ビットモード(UserInfo 加味済み)
	private final boolean	pulldownFlag;		// 4.3.3.0 (2008/10/01) 強制プルダウン化属性

	private final GUIAccessCount accessCount ;	// この画面へのアクセス統計を管理します。

	private int   level	;

	// 6.8.4.2 (2017/12/25) nextGui を、後入れ先出しに変更します。
	private final Deque<String> nextGui = new ConcurrentLinkedDeque<>();	// 6.8.4.2 (2017/12/25) nextGui を、後入れ先出しに変更します。

	/**
	 * コンストラクター
	 *
	 * 引数の bitMode は、UserInfo と加味済み
	 *
	 * @og.rev 4.3.0.0 (2008/07/04) ファイル入出力制御追加
	 * @og.rev 4.3.3.0 (2008/10/01) 強制プルダウンモード追加
	 *
	 * @param	guiData		画面データオブジェクトID
	 * @param	labelData	ラベルデータオブジェクト
	 * @param	bitMode	ビットモード配列 "--:000","-r:001","-w:010","mr:101","mw:110" に対応した数字(0,1,2,5,6)
	 */
	public GUIInfo( final GUIData	guiData ,
					final LabelData labelData ,
					final byte		bitMode ) {
		this.guiData	= guiData;
		this.labelData	= labelData;
		groupKeys		= StringUtil.csv2Array( guiData.getGroups() );

		menuFlag	= RoleMode.isMenu( bitMode );
		writeFlag	= RoleMode.isWrite( bitMode );
		pulldownFlag = RoleMode.isPulldown( bitMode ); // 4.3.3.0 (2008/10/01)
		accessCount = new GUIAccessCount( guiData.getGuiKey() ) ;
		this.bitMode = bitMode ;

		level = guiData.getGuiLevel();
	}

	/**
	 * 画面情報 画面ID を取得します。
	 *
	 * @return	画面ID
	 */
	public String getKey() {
		return guiData.getGuiKey();
	}

	/**
	 * 実行アドレス情報を取得します。
	 *
	 * @return	実行アドレス
	 */
	public String getAddress() {
		return guiData.getAddress();
	}

	/**
	 * トップからの実行アドレス情報を取得します。
	 * コンテキスト名とリンク区分属性を利用して、サーバートップからのアドレスを
	 * 返します。ただし、GUIリソースに、http://～ または、.～ から始まるアドレスは
	 * そのまま、なにも変換せずに返します。
	 * 実アドレスには、param属性の情報を付加します。param属性は、接続文字を用いずに
	 * そのまま連結されますので、/index.jsp?AAA=XX&amp;BBB=YY という感じで "/" から
	 * はじめます。
	 *
	 * http://AAAA  ⇒  http://AAAA
	 * ../../AAAA/  ⇒  ../../AAAA/
	 * AAAA         ⇒  /CONTEXT_NAME/KBLINK/AAAA/    param なし
	 * AAAA         ⇒  /CONTEXT_NAME/KBLINK/AAAA/index.jsp?AAA=XX&amp;BBB=YY  param あり
	 *
	 * @og.rev 3.5.5.0 (2004/03/12) 新規追加
	 * @og.rev 4.0.0.0 (2005/01/31) param属性追加
	 *
	 * @return	実行実アドレス
	 */
	public String getRealAddress() {
		return guiData.getRealAddress();
	}

	/**
	 * トップからの実行アドレス情報を取得します。
	 * コンテキスト名とリンク区分属性を利用して、サーバートップからのアドレスを
	 * 返します。ただし、GUIリソースに、http://～ または、.～ から始まるアドレスは
	 * そのまま、なにも変換せずに返します。
	 * 実アドレスには、param属性の情報を付加します。param属性は、接続文字を用いずに
	 * そのまま連結されますので、/index.jsp?AAA=XX&amp;BBB=YY という感じで "/" から
	 * はじめます。
	 * また、アドレスの最後がスラッシュ(/)で終了している場合は、page属性を追加します。
	 *
	 * http://AAAA  ⇒  http://AAAA
	 * ../../AAAA/  ⇒  ../../AAAA/
	 * AAAA         ⇒  /CONTEXT_NAME/KBLINK/AAAA/    param なし
	 * AAAA         ⇒  /CONTEXT_NAME/KBLINK/AAAA/index.jsp?AAA=XX&amp;BBB=YY  param あり
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加(param属性追加)
	 *
	 * @param    page          実行ページ(index.jsp など)
	 *
	 * @return	実行実アドレス
	 */
	public String getRealAddress( final String page ) {
		return guiData.getRealAddress( page );
	}

	/**
	 * 画面の表示順を取得します。
	 *
	 * @return	画面の表示順
	 */
	public int getSequence() {
		return guiData.getSeqno();
	}

	/**
	 * 画面の階層番号(レベル)を取得します。
	 * 画面階層は、
	 * 　0:予約階層(将来的にタブブラウザ対応時に使用
	 * 　1:トップ階層(通常のメニューの分類として表示されます。)
	 * 　2:選択階層(通常の折りたたみメニューの画面選択時に使用されます。)
	 * 　3以下:下位階層(通常の選択メニューとして、１段下げて表示されます。)
	 * です。
	 * なお、これらの意味は、実際にメニューを作成/表示するクラスに依存します。
	 *
	 * @return	画面の表示順
	 */
	public int getLevel() {
		return level;
	}

	/**
	 * 画面の階層番号(レベル)をアップします。
	 *
	 * これは、レベルが３の場合(階層時の隠しメニュー)をレベル２に
	 * することで、常に見えているメニューに格上げします。
	 * 具体的には、設定値が隠しメニューの場合に、アクセスするとレベル２へ格上げ
	 * することで、個人単位で、過去の履歴に応じたメニュー配置が可能になります。
	 */
	public void setLevelUp() {
		if( level == 4 ) { level = 3; } // 4.0.0.0 (2007/10/30)
	}

	/**
	 * 画面情報 メニュグループのオリジナルキー を取得します。
	 * メニュグループは、CSV形式で複数登録できます。
	 *
	 * @return	メニュ分類のキー
	 */
	public String getGroups() {
		return guiData.getGroups();
	}

	/**
	 * 指定の文字列がグループに含まれているかどうかを判定します。
	 * メニュグループは、CSV形式で複数登録できますので、そのうちの
	 * どれかに含まれていれば、true を返します。
	 * このメニューそのものに、グループが指定されていない場合は、
	 * デフォルトグループという扱いで、true を返します。
	 * 引数が、null または、ゼロ文字列の場合も、同様に、true を返します。
	 *
	 * @param	group	判定するグループ
	 *
	 * @return	グループに含まれているかどうか
	 */
	public boolean isGroupIn( final String group ) {
		if( groupKeys.length == 0 || group == null || group.isEmpty() ) {
			return true;
		}

		for( int i=0; i<groupKeys.length; i++ ) {
			if( group.equals( groupKeys[i] ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 画面情報 メニュ分類のオリジナルキー を取得します。
	 *
	 * @return	メニュ分類のキー
	 */
	public String getClassify() {
		return guiData.getClassify();
	}

	/**
	 * 画面情報 画面名称 を取得します。
	 * これは、加工前のラベルリソースに登録されている値です。
	 *
	 * @return	画面名称
	 */
	public String getLabel() {
		return labelData.getLabel();
	}

	/**
	 * 画面情報 画面名称(short) を取得します。
	 * この名称は、チップ表示付きの文字列を返します。
	 *
	 * @return	画面名称(short)
	 */
	public String getName() {
		return labelData.getShortLabel();
	}

	/**
	 * 画面情報 画面名称(long) を取得します。
	 * この名称は、チップ表示付きの文字列を返します。
	 *
	 * @return	画面名称(long)
	 */
	public String getLongName() {
		return labelData.getLongLabel();
	}

	/**
	 * 画面情報 ロール を取得します。
	 * ロールは、AAA|BBB|CCC と『|』の区切り文字で複数登録できます。
	 * ユーザーのロール(こちらも、XXX|YYY|AAAと複数登録可能)とマッチする
	 * ロールがあれば、その画面のアクセス許可があります。
	 * 読み書きと、メニュー表示は、アクセスモードで指定します。
	 *
	 * @return	ロール
	 */
	public String getRoles() {
		return guiData.getRoles();
	}

	/**
	 * アクセスモードを取得します。
	 *
	 * r,w,_ を各ロール毎に設定します。
	 * mr:メニューよりアクセスできる読取専用画面です。登録ボタンは表示されません。
	 * mw:メニューよりアクセスできる登録編集画面です。表示もします。
	 * -r:メニューに現れませんが、アクセスすることは可能です。読取専用。
	 * -w:メニューに現れませんが、アクセスすることは可能です。読み書き出来ます。
	 *
	 * この２文字ずつのセットが、各ロールに対応付けられたアクセス制御になります。
	 * ロールが、AAA|BBB|CCC|DDD で、モードが mw|mr|-r|-w であれば、
	 * AAA は、mw , BBB は、mr ，CCC は、-r ，DDD は -w と設定されたことになります。
	 * 特別に、２文字のみ登録された場合は、全ロールが同一モードに設定
	 * されたとみなします。
	 *
	 * @return	ロール毎のアクセスモード列(mr,mw,-r,-w の羅列)
	 */
	public String getMode() {
		return guiData.getMode();
	}

	/**
	 * 画面を表示する時のターゲット属性を取得します。
	 *
	 * @return	ターゲット
	 */
	public String getTarget() {
		return guiData.getTarget();
	}

	/**
	 * 画面を表示する時のパラメータ属性を取得します。
	 *
	 * @return	パラメータ
	 */
	public String getParam() {
		return guiData.getParam();
	}

	/**
	 * リンク区分属性を取得します。
	 *
	 * @og.rev 3.4.0.0 (2003/09/01) リンク区分(KBLINK)属性を追加。
	 *
	 * @return	リンク区分
	 */
	public String getKblink() {
		return guiData.getKblink();
	}

	/**
	 * 概要説明属性を取得します。
	 * 概要説明が設定されていない場合は、longName を返します。
	 *
	 * @og.rev 3.5.6.5 (2004/08/09) 概要説明(DESCRIPTION)属性を追加。
	 *
	 * @return	概要説明
	 */
	public String getDescription() {
		return labelData.getDescription() ;
	}

	/**
	 * 更新日時を取得します。
	 *
	 * @og.rev 5.3.3.0 (2011/03/01) 新規作成
	 *
	 * @return	更新日時
	 */
	public String getDyupd() {
		return guiData.getDyupd();
	}

	/**
	 * イメージアイコンのキーを返します。
	 *
	 * 画面にアイコンを追加する場合、jsp/menuImage フォルダに、画面ID と同じ名称の
	 * 画像ファイルを置く必要があります。
	 *
	 * ※ 6.3.8.4 (2015/10/09)
	 *    従来は、PARAM 属性に、IMAGE_KEY=XXXX と指定していましたが、
	 *    KBLINK(リンク区分) を使用するように変更しました。
	 *
	 * @og.rev 5.5.2.5 (2012/05/21) 新規追加
	 * @og.rev 6.3.8.4 (2015/10/09) KBLINK(リンク区分)を画面のイメージファイルに割り当てます。
	 *
	 * @return	イメージアイコンのキー
	 */
	public String getImageKey() {
		return guiData.getImageKey();
	}

	/**
	 * ロールモード情報を取得します。
	 *
	 * @og.rev 4.3.0.0 (2008/07/04) 新規追加
	 *
	 * @return	ロールモード
	 */
	public RoleMode getRoleMode() {
		return guiData.getRoleMode() ;
	}

	/**
	 * リードアクセス(読取り許可)の 可否を チェックします。
	 * アクセスチェックは、画面のロールをユーザーの
	 * それと比較して条件が含まれているかどうかを確認します。
	 * 条件が null (または０ストリング)の場合は, true となります。
	 * 条件の判断は、AND 条件です。
	 * さらに、その他の条件部分を判断して、OR 条件で先の結果と突き合わせます。
	 * ユーザーのロールが、 "root" の場合は,rw 属性のみのチェックで判断します。
	 *
	 * @og.rev 3.5.4.0 (2003/11/25) 引数にロールズを渡します。
	 *
	 * @return  アクセスOK：true  アクセス拒否：false
	 */
	public boolean isRead() {
		return menuFlag;
	}

	/**
	 * ライトアクセス(書込み許可)の 可否を チェックします。
	 * アクセスチェックは、画面のロールをユーザーの
	 * それと比較して条件が含まれているかどうかを確認します。
	 * 条件が null (または０ストリング)の場合は, true となります。
	 * 条件の判断は、AND 条件です。
	 * さらに、その他の条件部分を判断して、OR 条件で先の結果と突き合わせます。
	 * ユーザーのロールが、 "root" の場合は,rw 属性のみのチェックで判断します。
	 *
	 * @og.rev 3.5.4.0 (2003/11/25) 引数にロールズを渡します。
	 *
	 * @return  アクセスOK：true  アクセス拒否：false
	 */
	public boolean isWrite() {
		return writeFlag;
	}

	/**
	 * ボタンメニューにプルダウンを指定するのかをチェックします。
	 *
	 * @og.rev 4.3.3.0 (2008/10/01) 新規作成
	 *
	 * @return プルダウン化の場合true
	 */
	public boolean isPulldown() {
		return pulldownFlag;
	}

	/**
	 * 指定のユーザーロールに対するビット条件を取得します。
	 * この bitMode は、すでにユーザー単位に作成された値です。
	 *
	 * @og.rev 4.3.0.0 (2008/07/04) ロールモードマルチ対応
	 *
	 * @return アクセスビット
	 */
	public byte getBitMode() {
		return bitMode;
	}

	/**
	 * GUIInfoの属性文字列を取得します。
	 *
	 * ・KEY           画面ID
	 * ・ADDRESS       実行アドレス
	 * ・REALADDRESS   実行実アドレス
	 * ・SEQUENCE      表示順
	 * ・GROUPS        メニュグループ
	 * ・CLASSIFY      メニュ分類
	 * ・LEVEL         メニュ階層番号
	 * ・LABEL         画面名称
	 * ・NAME          画面名称(=SNAME)
	 * ・SNAME         画面名称(short)
	 * ・LNAME         画面名称(long)
	 * ・ROLES         ロール
	 * ・MODE          アクセスモード列(mr,mw,-r,-w の羅列)
	 * ・TARGET        ターゲット
	 * ・PARAM         設定値(パラメータ)
	 * ・KBLINK        リンク区分
	 * ・DESCRIPTION   概要説明
	 * ・IMAGEKEY      イメージキー
	 * ・DYUPD         更新日時
	 * ・ISREAD        読取り許可[true/false]
	 * ・ISWRITE       書込み許可[true/false]
	 *
	 * @og.rev 3.4.0.0 (2003/09/01) リンク区分(KBLINK)属性を追加。
	 * @og.rev 3.5.5.0 (2004/03/12) 実行実アドレス(REALADDRESS)属性を追加。
	 * @og.rev 3.5.6.5 (2004/08/09) 概要説明(DESCRIPTION)属性を追加。
	 * @og.rev 4.0.0.0   (2005/11/30) ISREAD,ISWRITE 属性を追加。
	 * @og.rev 5.3.3.0 (2011/03/01) 更新日時を追加
	 * @og.rev 5.5.2.5 (2012/05/21) IMAGEKEY 追加
	 * @og.rev 5.6.4.3 (2013/05/25) FAQ追加
	 * @og.rev 6.3.8.4 (2015/10/09) GE80(FAQﾃｰﾌﾞﾙ)の取得は廃止。(helpタグで行う)
	 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. Position literals first in String comparisons for EqualsIgnoreCase.
	 *
	 * @param	key  	キー
	 *
	 * @return	属性文字列の値
	 */
	public String getAttribute( final String key ) {
		final String rtn ;

		if( key == null ) { rtn = null; }
		else {
			if(      "KEY"         .equalsIgnoreCase( key ) ) { rtn = getKey(); }
			else if( "GUICLM"      .equalsIgnoreCase( key ) ) { rtn = labelData.getKey(); }
			else if( "ADDRESS"     .equalsIgnoreCase( key ) ) { rtn = getAddress(); }
			else if( "REALADDRESS" .equalsIgnoreCase( key ) ) { rtn = getRealAddress(); }
			else if( "SEQUENCE"    .equalsIgnoreCase( key ) ) { rtn = String.valueOf( getSequence() ); }
			else if( "GROUPS"      .equalsIgnoreCase( key ) ) { rtn = getGroups(); }
			else if( "CLASSIFY"    .equalsIgnoreCase( key ) ) { rtn = getClassify(); }
			else if( "LEVEL"       .equalsIgnoreCase( key ) ) { rtn = String.valueOf( getLevel() ); }
			else if( "LABEL"       .equalsIgnoreCase( key ) ) { rtn = getLabel(); }
			else if( "NAME"        .equalsIgnoreCase( key ) ) { rtn = getName(); }
			else if( "SNAME"       .equalsIgnoreCase( key ) ) { rtn = getName(); }
			else if( "LNAME"       .equalsIgnoreCase( key ) ) { rtn = getLongName(); }
			else if( "ROLE"        .equalsIgnoreCase( key ) ) { rtn = getRoles(); }
			else if( "ROLES"       .equalsIgnoreCase( key ) ) { rtn = getRoles(); }
			else if( "MODE"        .equalsIgnoreCase( key ) ) { rtn = getMode(); }
			else if( "TARGET"      .equalsIgnoreCase( key ) ) { rtn = getTarget(); }
			else if( "PARAM"       .equalsIgnoreCase( key ) ) { rtn = getParam(); }
			else if( "KBLINK"      .equalsIgnoreCase( key ) ) { rtn = getKblink(); }
			else if( "DESCRIPTION" .equalsIgnoreCase( key ) ) { rtn = getDescription(); }				// 3.5.6.5 (2004/08/09)
			else if( "IMAGEKEY"    .equalsIgnoreCase( key ) ) { rtn = getImageKey(); }					// 3.5.6.5 (2004/08/09)
			else if( "DYUPD"       .equalsIgnoreCase( key ) ) { rtn = getDyupd(); }						// 5.5.2.5 (2012/05/21)
			else if( "ISREAD"      .equalsIgnoreCase( key ) ) { rtn = String.valueOf( isRead() ); }		// 4.0.0 (2005/11/30)
			else if( "ISWRITE"     .equalsIgnoreCase( key ) ) { rtn = String.valueOf( isWrite() ); }	// 4.0.0 (2005/11/30)
	//		// 6.3.8.4 (2015/10/09) GE80(FAQﾃｰﾌﾞﾙ)の取得は廃止。(helpタグで行う)
	//		else if( "FAQ"         .equalsIgnoreCase( key ) ) { rtn = String.valueOf(isFaq()); }		// 5.6.4.3 (2013/05/24)
			else {
				final String errMsg = "属性文字列キーが不正です。 key=[" + key + "]"
							+ CR
							+ "予約語(" + YOYAKU + ") 以外は指定できません。" ;
				throw new HybsSystemException( errMsg );
			}
		}
		return rtn ;
	}

	/**
	 * GUIInfoの属性文字列の内部情報を返します。
	 * この内部情報の中には、getAttribute( String ) で取得できる管理情報です。
	 *
	 * @og.rev 4.0.0.0 (2004/12/31) 新規作成
	 * @og.rev 5.3.3.0 (2011/03/01) 更新日時を追加
	 * @og.rev 5.5.2.5 (2012/05/21) IMAGEKEY 追加
	 *
	 * @return	属性文字列のHybsEntryオブジェクト配列
	 * @og.rtnNotNull
	 */
	public HybsEntry[] getEntrys() {
		final List<HybsEntry> list = new ArrayList<>();

		list.add( new HybsEntry( "GUI.KEY"         , getAttribute( "KEY"         ) , "画面ID" ) );
		list.add( new HybsEntry( "GUI.GUICLM"      , getAttribute( "GUICLM"      ) , "画面カラムID" ) );
		list.add( new HybsEntry( "GUI.ADDRESS"     , getAttribute( "ADDRESS"     ) , "実行アドレス" ) );
		list.add( new HybsEntry( "GUI.REALADDRESS" , getAttribute( "REALADDRESS" ) , "実行実アドレス" ) );
		list.add( new HybsEntry( "GUI.SEQUENCE"    , getAttribute( "SEQUENCE"    ) , "表示順" ) );
		list.add( new HybsEntry( "GUI.GROUPS"      , getAttribute( "GROUPS"      ) , "メニュグループ" ) );
		list.add( new HybsEntry( "GUI.CLASSIFY"    , getAttribute( "CLASSIFY"    ) , "メニュ分類" ) );
		list.add( new HybsEntry( "GUI.LEVEL"       , getAttribute( "LEVEL"       ) , "メニュ階層番号" ) );
		list.add( new HybsEntry( "GUI.LABEL"       , getAttribute( "LABEL"       ) , "画面名称" ) );
		list.add( new HybsEntry( "GUI.NAME"        , getAttribute( "NAME"        ) , "画面名称(=SNAME)" ) );
		list.add( new HybsEntry( "GUI.SNAME"       , getAttribute( "SNAME"       ) , "画面名称(short)" ) );
		list.add( new HybsEntry( "GUI.LNAME"       , getAttribute( "LNAME"       ) , "画面名称(long)" ) );
		list.add( new HybsEntry( "GUI.ROLES"       , getAttribute( "ROLES"       ) , "ロール" ) );
		list.add( new HybsEntry( "GUI.MODE"        , getAttribute( "MODE"        ) , "アクセスモード列(mr,mw,-r,-w の羅列)" ) );
		list.add( new HybsEntry( "GUI.TARGET"      , getAttribute( "TARGET"      ) , "ターゲット" ) );
		list.add( new HybsEntry( "GUI.PARAM"       , getAttribute( "PARAM"       ) , "パラメータ" ) );
		list.add( new HybsEntry( "GUI.KBLINK"      , getAttribute( "KBLINK"      ) , "リンク区分" ) );
		list.add( new HybsEntry( "GUI.DESCRIPTION" , getAttribute( "DESCRIPTION" ) , "概要説明" ) );
		list.add( new HybsEntry( "GUI.IMAGEKEY"    , getAttribute( "IMAGEKEY"    ) , "イメージキー" ) );	// 5.5.2.5 (2012/05/21)
		list.add( new HybsEntry( "GUI.DYUPD"       , getAttribute( "DYUPD"       ) , "更新日時" ) );		// 5.3.3.0 (2011/03/01)
		list.add( new HybsEntry( "GUI.ISREAD"      , getAttribute( "ISREAD"      ) , "読取り許可[true/false]" ) );
		list.add( new HybsEntry( "GUI.ISWRITE"     , getAttribute( "ISWRITE"     ) , "書込み許可[true/false]" ) );

		return list.toArray( new HybsEntry[list.size()] );
	}

	/**
	 * データベース検索した数と、掛かった時間(ms)を、セットします。
	 * これは、セキュリティ上の監視フラグで、不必要に、大量の
	 * データが検索された場合や、不正なデータアクセスがあるかどうかを
	 * 監視するための統計情報を取得します。
	 * 画面オブジェクトは、各ユーザー毎に作成されているため、個々の
	 * ユーザー毎/画面毎のアクセス状況を見ることが可能になります。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 * @param  cnt データベース検索した数
	 * @param  time データベース検索した数
	 * @param  query そのときのSQL文
	 */
	public void addReadCount( final int cnt,final long time,final String query ) {
		accessCount.addReadCount( cnt,time,query );
	}

	/**
	 * データベース登録した数と、掛かった時間(ms)を、セットします。
	 * これは、セキュリティ上の監視フラグで、不必要に、大量の
	 * データが登録された場合や、不正なデータアクセスがあるかどうかを
	 * 監視するための統計情報を取得します。
	 * 画面オブジェクトは、各ユーザー毎に作成されているため、個々の
	 * ユーザー毎/画面毎のアクセス状況を見ることが可能になります。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 * @param  cnt データベース登録した数
	 * @param  time データベース検索した数
	 * @param  query そのときのSQL文
	 */
	public void addWriteCount( final int cnt,final long time,final String query ) {
		accessCount.addWriteCount( cnt,time,query );
	}

	/**
	 * この画面へのアクセス回数を、＋１します。
	 * アクセス回数は、このメソッドの呼び出し回数のことです。
	 * 現状では、result.jsp 画面でセットすることで、アクセス数を
	 * 数えることにします。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 */
	public void addAccessCount() {
		if( level == 4 ) { level = 3; } // 4.0.0.0 (2007/10/30)
		accessCount.addAccessCount();
	}

	/**
	 * エラー発生時の件数を＋１します。
	 * これは、エラー発生時に呼び出すことで、エラー件数をチェックすることが
	 * 可能になります。
	 * 一般にエラーには、予期するエラー(必須入力登録漏れ等)と、予期しないエラー
	 * がありますが、ここでは、Java の Exceptionが発生する予期しないエラーの
	 * 件数をカウントします。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 */
	public void addErrorCount() {
		accessCount.addErrorCount();
	}

	/**
	 * この画面のアクセス統計オブジェクトを取得します。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 * @return	アクセス統計オブジェクト
	 */
	public GUIAccessCount getGUIAccessCount() {
		return accessCount;
	}

	/**
	 * この画面の次にアクセスされた画面IDをセットします。
	 *
	 * これは、画面アクセスの履歴(順番)を管理する機能を提供します。
	 * 自分自身の次にアクセスされる画面IDの集合を管理することで
	 * QUERY画面上部のショートカットリンクに、次に使用する画面の
	 * リンクを用意することが可能になります。
	 *
	 * @og.rev 5.2.3.0 (2010/12/01) アクセス履歴管理
	 * @og.rev 6.8.4.2 (2017/12/25) nextGui を、後入れ先出しに変更します。
	 *
	 * @param	guiKey	この画面の次にアクセスされた画面ID
	 */
	public void setNextGuiKey( final String guiKey ) {
		// 自分自身の場合は、セットしない。
		if( guiKey != null && !guiKey.equals( getKey() ) ) {
			nextGui.remove( guiKey );			// 実装は、List なので、一旦削除(Setの代用のつもり)
			nextGui.addFirst( guiKey );			// 要素の最初に挿入
		}
	}

	/**
	 * この画面の次にアクセスされた画面IDのCSV文字列を取得します。
	 *
	 * これは、画面アクセスの履歴(順番)をCSV形式で取り出します。
	 * アクセス履歴を外部記憶媒体に出力する場合に使用します。
	 *
	 * @og.rev 5.2.3.0 (2010/12/01) アクセス履歴管理
	 * @og.rev 6.4.3.4 (2016/03/11) CSV形式の文字連結を、stream 経由で行います。
	 *
	 * @return	この画面の次にアクセスされた画面IDのCSV文字列
	 * @og.rtnNotNull
	 */
	public String getNextGuiKeys() {
		return nextGui.stream()
					.collect( Collectors.joining( "," ) );
	}

	/**
	 * この画面の次にアクセスされた画面IDの文字列配列で取得します。
	 *
	 * これは、画面アクセスの履歴(順番)を文字列配列で取り出します。
	 *
	 * @og.rev 5.2.3.0 (2010/12/01) アクセス履歴管理
	 *
	 * @return	この画面の次にアクセスされた画面IDの文字列配列
	 */
	public String[] getNextGuiArray() {
		// 6.9.8.0 (2018/05/28) FindBugs:java.util.concurrent のインスタンスで同期化している
		return nextGui.toArray( new String[nextGui.size()] );

//		final String[] rtnAry ;
//		synchronized( nextGui ) {
//			rtnAry = nextGui.toArray( new String[nextGui.size()] );
//		}
//
//		return rtnAry ;
	}

	/**
	 * 自然比較メソッド
	 * インタフェース Comparable の 実装に関連して、再定義しています。
	 * 登録されたシーケンス(画面の表示順)で比較します。
	 * equals メソッドでは、キーの同一性のみに着目して判定しています。
	 * この比較では、(運用上同一キーは発生しませんが)たとえ同一キーが存在した
	 * としても、その比較値が同じになることを保証していません。
	 *
	 * @param   other 比較対象のObject
	 *
	 * @return  このオブジェクトが指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数
	 * @throws  ClassCastException 引数が GUIInfo ではない場合
	 * @throws  IllegalArgumentException 引数が null の場合
	 */
	public int compareTo( final GUIInfo other ) {		// 4.3.3.6 (2008/11/15) Generics警告対応
		if( other == null ) {
			final String errMsg = "引数が、null です。" ;
			throw new IllegalArgumentException( errMsg );
		}
		return getSequence() - other.getSequence();		// 4.3.3.6 (2008/11/15) Generics警告対応
	}

	/**
	 * このオブジェクトと他のオブジェクトが等しいかどうかを示します。
	 * 画面は、画面IDが等しければ、言語や表示順に関係なく同一とみなされます。
	 * GUIInfo は、ユーザー個別に扱われ、そのグループには、key は唯一で、かつ
	 * 同一言語内で扱われるオブジェクトの為、同一とみなします。
	 *
	 * @param   object 比較対象の参照オブジェクト
	 *
	 * @return	引数に指定されたオブジェクトとこのオブジェクトが等しい場合は true、そうでない場合は false
	 */
	@Override
	public boolean equals( final Object object ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return object instanceof GUIInfo && getKey().equals( ((GUIInfo)object).getKey() );
	}

	/**
	 * オブジェクトのハッシュコード値を返します。
	 * このメソッドは、java.util.Hashtable によって提供されるような
	 * ハッシュテーブルで使用するために用意されています。
	 * equals( Object ) メソッドをオーバーライトした場合は、hashCode() メソッドも
	 * 必ず 記述する必要があります。
	 * この実装では、getKey().hashCode() と同値を返します。
	 *
	 * @return  このオブジェクトのハッシュコード値
	 */
	@Override
	public int hashCode() {
		return getKey().hashCode() ;
	}

	/**
	 * オブジェクトの識別子として，詳細な画面情報を返します。
	 *
	 * @og.rev 3.4.0.0 (2003/09/01) リンク区分(KBLINK)属性を追加。
	 * @og.rev 3.5.5.0 (2004/03/12) 実行アドレス(ADDRESS)属性を追加。
	 * @og.rev 5.3.3.0 (2011/03/01) 更新日時を追加
	 * @og.rev 5.5.2.5 (2012/05/21) IMAGEKEY 追加
	 *
	 * @return  詳細な画面情報
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE )
			.append( "key        :").append( getKey()           ).append( CR )	// 画面ID
			.append( "lvlclm     :").append( labelData.getKey() ).append( CR )	// 画面カラムID
			.append( "address    :").append( getAddress()       ).append( CR )	// 実行アドレス
			.append( "sequence   :").append( getSequence()      ).append( CR )	// 表示順
			.append( "groups     :").append( getGroups()        ).append( CR )	// メニュグループ
			.append( "classify   :").append( getClassify()      ).append( CR )	// メニュ分類
			.append( "level      :").append( getLevel()         ).append( CR )	// 階層レベル
			.append( "name       :").append( getName()          ).append( CR )	// 画面名称
			.append( "longName   :").append( getLongName()      ).append( CR )	// 画面名称(long)
			.append( "roles      :").append( getRoles()         ).append( CR )	// ロール
			.append( "mode       :").append( getMode()          ).append( CR )	// アクセスモード  "rwrwrw"
			.append( "target     :").append( getTarget()        ).append( CR )	// ターゲット
			.append( "kblink     :").append( getKblink()        ).append( CR )	// リンク区分
			.append( "description:").append( getDescription()   ).append( CR )	// 概要説明
			.append( "imageKey   :").append( getImageKey()      ).append( CR )	// イメージキー 5.5.2.5 (2012/05/21)
			.append( "dyupd      :").append( getDyupd()         ).append( CR );	// 更新日時
		return rtn.toString();
	}
}
