001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.taglib;
017
018import static org.opengion.fukurou.util.StringUtil.nval;
019
020import java.util.List;
021import java.util.ArrayList;
022import java.util.Map;
023import java.util.LinkedHashMap;
024import java.util.Collections;
025import java.util.Locale ;                                                               // 6.7.6.1 (2017/03/17)
026import java.util.concurrent.ConcurrentMap;                              // 6.7.8.0 (2017/04/21)
027import java.util.concurrent.ConcurrentHashMap;                  // 6.7.8.0 (2017/04/21)
028
029import org.opengion.hayabusa.common.HybsSystem;
030import org.opengion.hayabusa.db.DBTableModel;
031import org.opengion.fukurou.util.ToString;                              // 6.8.5.0 (2018/01/09)
032
033import static org.opengion.hayabusa.taglib.ValueMapParamTag.VMP_KEYS;   // 6.7.8.0 (2017/04/21)
034
035/**
036 * DBTableModelオブジェクトから、指定のキー情報と、レコードから、Mapオブジェクトを作成し、それを、
037 * BODY部のフォーマットに対応して、出力します。
038 *
039 * valueタグの、command="MAPOBJ" や、ALL_MAPOBJ に相当する処理を、複数キーと行レベルのデータで
040 * 管理します。
041 *
042 * 設定した値は、Mapを優先した、特殊な、{@XXXX} 形式で 取り出すことができます。
043 *
044 * keys で、CSV形式でカラム名を指定し、これらを、連結した文字列を、Mapのキー情報に使います。
045 * Mapの値情報は、そのレコードの配列になります。
046 * keys を指定しない場合は、最初のカラムの値が、キーになります。
047 * キーが重複する場合、先に現れたデータが優先されます。
048 *
049 * 値の取出し方法は、キーに対して、{@XXXX} 形式を、適用します。
050 * Map に存在しないキーは、リクエスト変数や、通常のvalus変数を見ます。
051 * valClm で、{@XXXX} 形式で取り出す値のカラムを指定できます。
052 * valClm を指定しない場合は、2番目のカラムを使用します。
053 *
054 * 特殊機能
055 *   ・holdTag属性:{@XXXX} を、指定のタグで囲います。
056 *     例えば、holdTag="span" とすると、<span class="YYYYの値" >XXXXの値</span>
057 *     という文字列を作成します。
058 *   ・clsClms属性:先の指定のタグで囲う場合、そのタグのclass属性を指定できます。
059 *     複数指定した場合は、スペースで、連結します。
060 *   ・{@XXXX cls="B"} とすると、個別の clsClms の値を使用せず、この値を、class属性として使います。
061 *     clsClms と同様に、holsTag属性を指定しておく必要があります。
062 *   ・tipsClms属性:先の指定のタグで囲う場合、そのタグのtitle属性を指定できます。
063 *     マウスオーバーで、チップス表示されます。
064 *   ・{@XXXX tips="YYYY"} とすると、個別の tipsClms の値を使用せず、この値を、title属性として使います。
065 *     tipsClms と同様に、holsTag属性を指定しておく必要があります。
066 *   ・nnClms属性:この属性で指定された値が、nullの場合、{@XXXX} の解析を行いません。
067 *     正確に言うと、Mapに取り込みません。この場合、先のholdTag属性で指定したタグそのものも
068 *     出力しません。
069 *   ・{@XXXX* str="val"} とすると、キーワードのあいまい検索部分に、strで指定した
070 *     val文字列が存在する場合のみ有効とします。
071 *   ・キーに対して、Mapは、行データ全部を持っています。{@XXXX} は、最初のカラムの値です。
072 *   ・2番目を取得する場合は、{@XXXX 1}と、3番目は、{@XXXX 2}と指定します。
073 *   ・{@XXXX*} を指定すると、キーのあいまい検索で、キーそのものを複数選択することが出来ます。
074 *     この場合、spanタグで囲う機能と併用すると、複数のspanタグを持つ文字列を合成できます。
075 *     この特定のタグは、holdTag 属性で指定します。
076 *     このキーのあいまい検索で、表示する順番は、DBTableModelでMapに読み込んだ順番になります。
077 *     {@XXXX* 2} のような、取得カラムの指定も可能です。
078 *     取得カラムの指定も可能ですが、カラム番号は、常に一番最後に記述してください。
079 *   ・{@XXXX!*} とすると、表示する順番を、逆順にすることが出来ます。取得カラムの指定も可能です。
080 *   ・{@$XXXX} とすると、holdTagも、clsClms も使用せず、設定値のみ出力します。
081 *     この場合は、固定値になるため、holsTagも、clsClms も使用しません。
082 *   ・{@*XXXX!*} とすると、キーのあいまい指定の残り部分の文字列を出力します。連番の場合の番号を取り出せます。
083 *   ・{@^XXXX} とすると、request.getAttribute()の値を優先して使用します。{@^XXXX*}などのあいまい指定も可能です。
084 *     この場合、オリジナルのキーは、DBTableModel上に必要です。値の入れ替えのみ、行う感じです。
085 *
086 * ※ このタグは、Transaction タグの対象です。
087 *
088 * @og.formSample
089 * ●形式:<og:valueMap />
090 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を特殊な方法で解析します)
091 *
092 * ●Tag定義:
093 *   <og:valueMap
094 *       keys               【TAG】パラメータから取り出すキーとなるカラム名を、CSV形式で指定します(初期値:最初のカラム)
095 *       valClm             【TAG】パラメータから取り出す値のカラム名を指定します(初期値:2番目のカラム)
096 *       holdTag            【TAG】値の前後を、指定のタグで挟みます
097 *       clsClms            【TAG】holdTagを使用するとき、そのタグの属性にclass属性を出力する場合のカラム名をCSV形式で指定します
098 *       tipsClms           【TAG】holdTagを使用するとき、そのタグの属性にtitle属性を出力する場合のカラム名をCSV形式で指定します
099 *       nnClms             【TAG】パラメータが NULL の時に、設定しないカラム名を、CSV形式で指定します
100 *       reqAttUpClms       【TAG】{@^XXXX}使用時に request.getAttribute() をセットすると同時に設定するカラム名をCSV形式で指定します 6.9.2.0 (2018/03/05)
101 *       selectedAll        【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:true)
102 *       separator          【TAG】キーとなるカラム名の値を連結する項目区切り文字をセットします(初期値:"_")
103 *       tableId            【TAG】sessionから取得する DBTableModelオブジェクトの ID(初期値:HybsSystem.TBL_MDL_KEY)
104 *       scope              【TAG】DBTableModelオブジェクトを取得する場合の、scope(初期値:session)
105 *       xssCheck           【TAG】パラメータの HTMLTag開始/終了文字(><) 存在チェックを実施するかどうか[true/false]を設定します (初期値:USE_XSS_CHECK[=true])
106 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null)
107 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null)
108 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない)
109 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない)
110 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
111 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
112 *   >   ... Body ...
113 *   </og:valueMap>
114 *
115 * ●使用例
116 * <og:query command="{@command}" debug="{@debug}" maxRowCount="{@maxRowCount}">
117 *         select CLM,NAME_JA,LABEL_NAME,KBSAKU,SYSTEM_ID,LANG,
118 *                 FGJ,USRSET,DYSET,USRUPD,DYUPD
119 *         from GF41
120 *     <og:where>
121 *         <og:and value = "SYSTEM_ID  =  '{@SYSTEM_ID}'"  />
122 *         <og:and value = "LANG       =  '{@LANG}'"       />
123 *         <og:and value = "CLM        like '{@CLM}'"      />
124 *         <og:and value = "NAME_JA    like '{@NAME_JA}'"  />
125 *         <og:and value = "LABEL_NAME like '{@LABEL_NAME}'" />
126 *         <og:and value = "KBSAKU     =    '{@KBSAKU}'"   />
127 *     </og:where>
128 *     <og:appear startKey = "order by" value = "{@ORDER_BY}"
129 *                 defaultVal = "SYSTEM_ID,CLM,LANG" />
130 * </og:query>
131 *
132 * <og:valueMap keys="SYSTEM_ID,CLM" holdTag="div" separator="_" clsClms="LANG,FGJ" >
133 * {@XX_AA0001} <br />   SYSTEM_IDとCLMの値を、separatorで連結。値は、キーの次(LABEL_NAME)
134 * {@XX_AA0001 1} <br /> 行番号の2番目(上のSQLではNAME_JA)の値
135 * {@XX_AA0001 2} <br /> 行番号の3番目(上のSQLではLABEL_NAME)の値
136 *
137 * {@XX_AA001* 2} <br /> キーの前方一致する行の3番目の値
138 *
139 * {@XX_AA000!* 1} キーの前方一致する行の2番目の値を、逆順で表示
140 *
141 * </og:valueMap>
142 *
143 *  ・ キーは、select文の1番目のカラム
144 *     <og:og:valueMap >  ・・・フォーマット・・・ </og:valueMap>
145 *  ・ キーが複数で、ユニークになる。(keys)
146 *     <og:og:valueMap keys="SYSTEM_ID,CLM" >  ・・・フォーマット・・・ </og:valueMap>
147 *  ・ 値をdivタグで囲う。(holdTag)
148 *     <og:og:valueMap keys="SYSTEM_ID,CLM" holdTag="div" >  ・・・フォーマット・・・ </og:valueMap>
149 *  ・ キーの連結のセパレータを指定。(separator)
150 *     <og:og:valueMap keys="SYSTEM_ID,CLM" holdTag="div" separator="_" >  ・・・フォーマット・・・ </og:valueMap>
151 *  ・ 値をdivタグで囲う時に、クラス属性を追加します。(clsClms)
152 *     <og:og:valueMap keys="SYSTEM_ID,CLM" holdTag="div" separator="_" clsClms="LANG,FGJ" >  ・・・フォーマット・・・ </og:valueMap>
153 *  ・ 値をdivタグで囲う時に、チップス表示(title属性)を追加します。(tipsClms)
154 *     <og:og:valueMap keys="SYSTEM_ID,CLM" holdTag="div" separator="_" clsClms="LANG,FGJ" tipsClms="NAME_JA,LABEL_NAME" >  ・・・フォーマット・・・ </og:valueMap>
155 *
156 * @og.group その他部品
157 * @og.rev 6.7.1.0 (2017/01/05) 新規作成
158 *
159 * @version  6.7
160 * @author   Kazuhiko Hasegawa
161 * @since    JDK8.0,
162 */
163public class ValueMapTag extends CommonTagSupport {
164        /** このプログラムのVERSION文字列を設定します。   {@value} */
165        private static final String VERSION = "7.4.2.3 (2021/06/09)" ;
166        private static final long serialVersionUID = 742320210609L ;
167
168        private static final String CLS_KEY  = "cls=" ;         // 6.7.3.0 (2017/01/27) cls指定のキーワード
169        private static final String TIPS_KEY = "tips=" ;        // 6.7.3.0 (2017/01/27) tips指定のキーワード
170        private static final String STR_KEY  = "str=" ;         // 6.8.0.1 (2017/06/30) str指定のキーワード
171
172        // 8.1.0.0 (2021/12/28) HTML5 準拠に見直し(type="text/css" 不要)
173//      private static final String NONE1       = "<style type=\"text/css\">." ;        // 6.7.8.0 (2017/04/21)
174        private static final String NONE1       = "<style>." ;  // 6.7.8.0 (2017/04/21)
175        private static final String NONE2       = " { display : none ;} </style>" ;     // 6.7.8.0 (2017/04/21)
176
177        // 6.9.8.0 (2018/05/28) FindBugs:直列化可能クラスの非 transient で非直列化可能なインスタンスフィールド
178        private transient       DBTableModel table              ;
179
180        private String          tableId         = HybsSystem.TBL_MDL_KEY;
181        private boolean         selectedAll     = true;
182        private String          keys            ;
183        private String          valClm          ;                               // 6.7.2.0 (2017/01/16)
184        private String          nnClms          ;                               // このカラムの値が、nullのレコードは、使用しません。
185        private String          holdTag         ;                               // nullの場合は、なにもはさまない。
186        private String          clsClms         ;                               // holdTagで指定したタグの属性に、class属性を追加します。
187        private String          tipsClms        ;                               // 6.7.3.0 (2017/01/27) holdTagで指定したタグの属性に、title属性を追加します。
188        private String          reqAttUpClms;                           // 6.9.2.0 (2018/03/05) request.getAttribute() をセットすると同時に設定するカラム名
189        private String[]        reqAttClms      ;                               // 6.9.2.0 (2018/03/05) reqAttUpClms を、配列に分解したもの
190        private String          scope           = "session";    // "request","session"
191        private String          separator       = "_";                  // 項目区切り文字
192        private boolean         xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" ); // 5.1.7.0 (2010/06/01) XSS対策
193
194        private int[]           clsClmsNo       ;                               // clsClmsが指定されない場合は、長さゼロの配列
195        private int[]           tipsClmsNo      ;                               // 6.7.3.0 (2017/01/27) tipsClmsが指定されない場合は、長さゼロの配列
196        private int[]           reqAttClmsNo;                           // 6.9.2.0 (2018/03/05) reqAttUpClmsが指定されない場合は、長さゼロの配列
197        private int                     valClmNo        = 1;                    // valClmが指定されない場合は、2番目のカラム(=1)の値を使用します。
198
199        private String          body            ;                               // パラメータ処理済みのBODY部分の文字列
200        private String          restChangeKey   ;                       // ValueMapParamTag で置き換え処理を行うキーワード
201        private boolean         useNoneClsKey   ;                       // mapObj の残り処理をおこなうかどうか(true:行う)
202
203        // synchronizedMap にする必要性があるのか無いのか、よく判っていません。
204        /** Collections.synchronizedMap で、同期します。テーブルの行の取得順に、Mapに追加していきます。(先に登録したデータが有効) */
205        private final Map<String,String[]> mapObj = Collections.synchronizedMap( new LinkedHashMap<>() );
206
207        // ValueMapParamTagから受け取った、各種設定情報を、管理するMapオブジェクト。
208        private final ConcurrentMap<VMP_KEYS,String> paramMap = new ConcurrentHashMap<>();
209
210        // ValueMapParamTag で使用する、未使用のキーワードを管理するMap
211        /** Collections.synchronizedMap で、同期します。テーブルの行の取得順に、Mapに追加していきます。(先に登録したデータが有効) */
212        private final Map<String,String[]> restMap = Collections.synchronizedMap( new LinkedHashMap<>() );
213
214        /**
215         * デフォルトコンストラクター
216         *
217         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
218         *
219         */
220        public ValueMapTag() { super(); }               // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
221
222        /**
223         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
224         *
225         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
226         *
227         * @return      後続処理の指示
228         */
229        @Override
230        public int doStartTag() {
231                if( useTag() ) {
232                        useXssCheck( xssCheck );
233                        table = (DBTableModel)getObject( tableId );
234                        if( table != null && table.getRowCount() > 0 && table.getColumnCount() > 0 ) {
235                                makeMapObj( table );            // Body の評価前にMapを作成する必要がある。
236
237                                return EVAL_BODY_BUFFERED ;     // Body を評価する
238                        }
239                }
240                return SKIP_BODY ;                                      // Body を評価しない
241        }
242
243        /**
244         * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。
245         *
246         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
247         *
248         * @return      後続処理の指示(SKIP_BODY)
249         */
250        @Override
251        public int doAfterBody() {
252                body = getBodyString();
253
254                return SKIP_BODY ;
255        }
256
257        /**
258         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
259         *
260         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
261         *
262         * @return      後続処理の指示
263         */
264        @Override
265        public int doEndTag() {
266                debugPrint();           // 4.0.0 (2005/02/28)
267                if( useTag() && body != null ) {
268                        jspPrint( body );
269                }
270
271                // mapObj の残り処理が必要かどうか。mapObj が空で、NONE_CLS_KEY が存在する場合は、残を隠す。
272                if( useNoneClsKey ) {
273                        final String noneClassKey = paramMap.get( VMP_KEYS.NONE_CLS_KEY );
274                        jspPrint( NONE1 + noneClassKey + NONE2 );
275                }
276
277                return EVAL_PAGE ;
278        }
279
280        /**
281         * タグリブオブジェクトをリリースします。
282         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
283         *
284         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
285         * @og.rev 6.7.2.0 (2017/01/16) valClm 追加
286         * @og.rev 6.7.3.0 (2017/01/27) tipsClms 追加
287         * @og.rev 6.7.8.0 (2017/04/21) valueMapParam関連
288         * @og.rev 6.9.2.0 (2018/03/05) reqAttUpClms 追加
289         */
290        @Override
291        protected void release2() {
292                super.release2();
293                table           = null;
294                tableId         = HybsSystem.TBL_MDL_KEY;
295                selectedAll     = true;
296                keys            = null;
297                valClm          = null;                 // 6.7.2.0 (2017/01/16) 新規作成
298                nnClms          = null;                 // 6.7.2.0 (2017/01/16) 名称変更
299                holdTag         = null;
300                clsClms         = null;                 // 6.7.3.0 (2017/01/27) 追加
301                tipsClms        = null;                 // 6.7.2.0 (2017/01/16) 名称変更
302                reqAttUpClms= null;                     // 6.9.2.0 (2018/03/05) request.getAttribute() をセットすると同時に設定するカラム名
303                reqAttClms      = null;                 // 6.9.2.0 (2018/03/05) reqAttUpClms を、配列に分解したもの
304                scope           = "session";    // DBTableModel の取得先のscope
305                separator       = "_";
306                xssCheck        = HybsSystem.sysBool( "USE_XSS_CHECK" );        // 5.1.7.0 (2010/06/01) XSS解除対応
307                body            = null;
308                mapObj.clear();
309
310                clsClmsNo       = null;                 // clsClmsが指定されない場合は、長さゼロの配列
311                tipsClmsNo      = null;                 // 6.7.3.0 (2017/01/27) tipsClmsが指定されない場合は、長さゼロの配列
312                reqAttClmsNo= null;                     // 6.9.2.0 (2018/03/05) reqAttUpClmsが指定されない場合は、長さゼロの配列
313                valClmNo        = 1;                    // valClmが指定されない場合は、2番目のカラム(=1)の値を使用します。
314
315                paramMap.clear();                       // 6.7.8.0 (2017/04/21) valueMapParam関連
316                restMap.clear();                        // 6.7.8.0 (2017/04/21) valueMapParam関連
317                restChangeKey   = null ;        // ValueMapParamTag で置き換え処理を行うキーワード
318                useNoneClsKey   = false ;       // mapObj の残り処理をおこなうかどうか(true:行う)
319        }
320
321        /**
322         * 指定のスコープの内部キャッシュ情報に、DBTableModel の選択された値を登録します。
323         *
324         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
325         * @og.rev 6.7.2.0 (2017/01/16) valClm 追加
326         * @og.rev 6.7.3.0 (2017/01/27) tipsClms 追加
327         * @og.rev 6.7.8.0 (2017/04/21) valueMapParam関連
328         * @og.rev 6.9.2.0 (2018/03/05) reqAttUpClms 追加
329         *
330         * @param table         DBTableModelオブジェクト
331         */
332        private void makeMapObj( final DBTableModel table ) {
333                final int[] rowNo = getParameterRows();
334                if( rowNo.length == 0 ) { return; }
335
336                final int[] keysNo              = getClmNos( table,keys    , 0 );               // keysが指定されない場合は、先頭カラムを使用します。
337                final int[] nnClmsNo    = getClmNos( table,nnClms  ,-1 );               // nnClmsが指定されない場合は、長さゼロの配列
338                                        clsClmsNo       = getClmNos( table,clsClms ,-1 );               // clsClmsが指定されない場合は、長さゼロの配列
339                                        tipsClmsNo      = getClmNos( table,tipsClms,-1 );               // tipsClmsが指定されない場合は、長さゼロの配列
340                                        reqAttClmsNo= getClmNos( table,reqAttUpClms,-1 );       // 6.9.2.0 (2018/03/05) reqAttClmsが指定されない場合は、長さゼロの配列
341
342                if( reqAttUpClms != null && !reqAttUpClms.isEmpty() ) {                 // 6.9.2.0 (2018/03/05) reqAttUpClms を、配列に分解したもの
343                        reqAttClms = reqAttUpClms.split( "," );
344                }
345
346                for( int j=0; j<rowNo.length; j++ ) {
347                        final String[] rowData = table.getValues( j );
348
349                        // まず、nullチェックして、対象行かどうかを判定する。
350                        if( isNotNullCheck( rowData , nnClmsNo ) ) {
351                                // Map に登録するキーを連結して作成します。
352                                final String mapkey = getAppendKeys( rowData , keysNo , separator );
353                                mapObj.computeIfAbsent( mapkey, k -> rowData );         // まだ値に関連付けられていない場合、追加します。(先に登録したデータが有効)
354                //              mapObj.put( mapkey, rowData );                                          // 後で登録したデータが、有効になります。
355                        }
356                }
357                restMap.putAll( mapObj );               // 6.7.8.0 (2017/04/21) 一旦、すべてのMapをコピーします。
358
359                // valClmが指定されない場合は、2番目のカラム(=1)の値を使用します。
360                valClmNo = valClm == null || valClm.isEmpty() ? 1 : table.getColumnNo( valClm.trim() ); // 存在しない場合は、Exception
361        }
362
363        /**
364         * カラム名のCSV文字列を、DBTableModel の列番号の配列に変換します。
365         *
366         * カラム名のCSV文字列が、無指定の場合、no で指定するカラム番号を
367         * デフォルトとして使用します。no が、マイナスの場合は、長さゼロの
368         * 配列を返します。
369         *
370         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
371         * @og.rev 6.7.2.0 (2017/01/16) カラム番号の取り方を変更
372         *
373         * @param       table           DBTableModelオブジェクト
374         * @param       clms            カラム名のCSV文字列( nullではない )
375         * @param       no                      clmsが、nullか、空文字の場合の、カラム番号
376         * @return      カラム名の列番号の配列
377         */
378        private int[] getClmNos( final DBTableModel table , final String clms , final int no ) {
379                final int[] clmNo ;
380                if( clms == null || clms.isEmpty() ) {
381                        if( no < 0 ) { clmNo = new int[0]; }                    // 長さゼロの配列
382                        else         { clmNo = new int[] { no }; }              // 指定のカラム番号を持つ配列。
383                }
384                else {
385                        final String[] clmAry = clms.split( "," );
386                        clmNo = new int[clmAry.length];
387                        for( int i=0; i<clmAry.length; i++ ) {
388                                clmNo[i] = table.getColumnNo( clmAry[i].trim() );       // 存在しない場合は、Exception
389                        }
390                }
391
392                return clmNo;
393        }
394
395        /**
396         * 指定のカラムの値のすべてが、nullか、空文字列でない場合は、true を返します。
397         *
398         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
399         *
400         * @param       rowData 行データ
401         * @param       clmNo   カラム番号配列
402         * @return      nullか、空文字列でない場合は、true
403         */
404        private boolean isNotNullCheck( final String[] rowData , final int[] clmNo ) {
405                boolean rtn = true;                                             // カラムがない場合は、true になります。
406
407                // 7.2.9.4 (2020/11/20) PMD:This for loop can be replaced by a foreach loop
408                for( final int clm : clmNo ) {
409                        final String val = rowData[ clm ];
410//              for( int i=0; i<clmNo.length; i++ ) {
411//                      final String val = rowData[ clmNo[i] ];
412                        if( val == null || val.isEmpty() ) {
413                                rtn = false;
414                                break;
415                        }
416                }
417                return rtn;
418        }
419
420        /**
421         * Mapのキーとなるキーカラムの値を連結した値を返します。
422         *
423         * @param       rowData 行データ
424         * @param       clmNo   カラム番号配列
425         * @param       sep             結合させる文字列
426         *
427         * @return      Mapのキーとなるキーカラムの値を連結した値
428         * @og.rtnNotNull
429         */
430        private String getAppendKeys( final String[] rowData , final int[] clmNo , final String sep ) {
431                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
432
433                if( clmNo.length > 0 ) {
434                        buf.append( rowData[ clmNo[0] ] );                              // 最初のひとつ目
435                        for( int i=1; i<clmNo.length; i++ ) {
436                                final String val = rowData[ clmNo[i] ];
437                                if( val != null && !val.isEmpty() ) {
438                                        buf.append( sep ).append( val );
439                                }
440                        }
441                }
442                return buf.toString().trim();
443        }
444
445        /**
446         * Mapの値となる値カラムに対応する文字列配列を返します。
447         *
448         * ここでは、行データに対して、配列の添え字(カラム番号)を元に、値を求めます。
449         * その際、holdTag や、clsClms で指定したクラス名などの付加情報もセットします。
450         * さらに、{&#064;$XXXX} などの、holdTagの抑止(生データを返す) 処理を行います。
451         *
452         * @param       rowData         行の配列データ
453         * @param       val                     値データ
454         * @param       isNormal        holdTagを使用せず、ノーマル状態の値を出力するかどうか[true:ノーマルの値]
455         * @param       cls                     clsClmsNoの使用を抑止し、指定の値を、class属性にセットします。(nullはclsClmsNoを使用、isEmpty() は、classの削除、それ以外は置き換え)
456         * @param       tips            tipsClmsNoの使用を抑止し、指定の値を、title属性にセットします。(nullはtipsClmsNoを使用、isEmpty() は、titleの削除、それ以外は置き換え)
457         * @param       sufix           キーのあいまい指定時に、あいまいキー以降の文字列を指定します。あれば、その値を使用します。
458         *
459         * @og.rev 6.7.3.0 (2017/01/27) tips 追加
460         *
461         * @return      Mapのキーに対応する修飾した値
462         * @og.rtnNotNull
463         */
464        private String getMapVals( final String[] rowData , final String val , final boolean isNormal , final String cls , final String tips , final String sufix ) {
465                String rtnVal = sufix == null || sufix.isEmpty() ? val : sufix ;
466
467                if( !isNormal && holdTag != null && !holdTag.isEmpty() ) {
468                        // 毎行ごとに、class属性の値は異なる。
469                        final String clazz = cls  == null ? getAppendKeys( rowData,clsClmsNo ," " ) : cls ;             // class 属性は、スペースで連結
470                        final String title = tips == null ? getAppendKeys( rowData,tipsClmsNo," " ) : tips ;    // title 属性は、スペースで連結
471
472                        final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
473                        buf.append( '<' ).append( holdTag );
474                        if( !clazz.isEmpty() ) { buf.append( " class=\"" ).append( clazz ).append( '"' ); }
475                        if( !title.isEmpty() ) { buf.append( " title=\"" ).append( title ).append( '"' ); }
476                        buf.append( '>' ).append( rtnVal ).append( "</" ).append( holdTag ).append( '>' );
477
478                        rtnVal = buf.toString();
479                }
480                return rtnVal ;
481        }
482
483        /**
484         * リクエスト情報の文字列を取得します。
485         *
486         * これは、CommonTagSupportの#getRequestValue( String ) を
487         * オーバーライドして、Mapから、設定値を取得します。
488         *
489         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
490         * @og.rev 6.7.2.0 (2017/01/16) valClm 追加
491         * @og.rev 6.7.3.0 (2017/01/27) tips 追加
492         * @og.rev 6.7.6.1 (2017/03/17) 値データを渡すようにします(isAttVal の追加対応)。
493         * @og.rev 6.7.8.0 (2017/04/21) valueMapParam関連
494         * @og.rev 6.8.0.1 (2017/06/30) str指定のキーワード
495         * @og.rev 6.9.2.0 (2018/03/05) reqAttUpClms 追加
496         *
497         * @param    key キー
498         *
499         * @return   リクエスト情報の文字列
500         * @see         CommonTagSupport#getRequestValue( String )
501         */
502        @Override
503        protected String getRequestValue( final String key ) {
504                if( key.equals( restChangeKey ) ) { return makeRestValue(); }           // 6.7.8.0 (2017/04/21)
505
506                // {@!XXXX} や、{@*XXXX!*} の場合のキー対応。最初に行う。
507                final char ch1 = key.charAt(0);
508                final boolean isNormal = ch1 == '$' ;           // holdTag を使わず、値そのものを出します。
509                final boolean isSufix  = ch1 == '*' ;           // あいまい検索時に、あいまいで削除された部分文字列を使うかどうか。
510                final boolean isAttVal = ch1 == '^' ;           // 値を、request.getAttribute()の値を優先して使用します。
511
512                final StringBuilder keyBuf = new StringBuilder( key.trim() );
513
514                if( isNormal || isSufix || isAttVal ) { keyBuf.deleteCharAt( 0 ); }             // 先頭の文字を削除
515
516                // カラム番号の取得のための分割。存在する場合は、必ず一番最後にします。
517                final int ad1  = keyBuf.lastIndexOf( " " );             // 後ろから検索して、スペースで分割
518                int       vcNo = valClmNo;
519                if( ad1 > 0 ) {
520                        // 必要かどうかはともかく、NumberFormatException で、判定すると、遅くなる気がする。
521                        final char ch = keyBuf.charAt( ad1 + 1 );       // 数字であろう先頭文字
522                        if( '0' <= ch && ch <= '9' ) {
523                                try {
524                                        vcNo   = Integer.parseInt( keyBuf.substring( ad1 + 1 ) );
525                                        keyBuf.setLength( ad1 );                                                                        // スペースが残っている可能性がある
526                                }
527                                catch( final NumberFormatException ex ) {               // 数値変換失敗時は、普通のパラメータだった場合。
528                                        System.err.println( ex.getMessage() );  // 8.0.0.0 (2021/07/31)
529//                                      ;
530        //                              vcNo   = valClmNo;              // vcNo は、セットする前にException が発生している。
531        //                              mapkey = key;                   // mapkey は、スペースも含むすべてのキーになる。・・・・・ NumberFormatException が先なので、setLength されていない。
532                                }
533                        }
534                }
535
536                // cls="B" 属性の取得
537                final String cls = getExtParam( keyBuf,CLS_KEY );               // 6.8.0.1 (2017/06/30) 関数化
538
539                // 6.7.3.0 (2017/01/27) tips="YYYY" 属性の取得
540                final String tips = getExtParam( keyBuf,TIPS_KEY );             // 6.8.0.1 (2017/06/30) 関数化
541
542                // 6.8.0.1 (2017/06/30) str指定のキーワード。先に取り除かないと、type 判定時の endsWith が効かない。
543                final String instr = getExtParam( keyBuf,STR_KEY );
544
545                // 中途半端に、スペースが残っていると厄介なので、削除しておきます。
546                final String mapkey = keyBuf.toString().trim();
547
548                // type==0 は、オリジナル。それ以外は、キーから取り除く文字数
549                final int type = mapkey.endsWith( "!*" ) ? 2 : mapkey.endsWith( "*" ) ? 1 : 0 ;
550
551                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
552                if( type == 0 ) {
553                        final String[] rowData = mapObj.get( mapkey );
554                        restMap.remove( mapkey );                                                       // 6.7.8.0 (2017/04/21) valueMapParam関連
555
556                        if( rowData != null && vcNo < rowData.length ) {
557                                // isAttVal == true のときは、RequestAttribute から取り出すが、キーは、大文字になっているので注意。
558                                // 6.9.2.0 (2018/03/05) DBTableModel への値のフィードバック
559//                              final String val = isAttVal ? nval( (String)getRequestAttribute( mapkey.toUpperCase(Locale.JAPAN) ) , rowData[vcNo] ) : rowData[vcNo] ;
560                                if( isAttVal ) {
561                                        // isAttVal == true のときは、RequestAttribute から取り出すが、キーは、大文字になっているので注意。
562                                        final String attVal = (String)getRequestAttribute( mapkey.toUpperCase(Locale.JAPAN) );
563                                        if( attVal != null && !attVal.isEmpty() ) {
564                                                rowData[vcNo] = attVal; // RequestAttribute をテーブルに戻す。
565                                                for( int i=0; i<reqAttClmsNo.length; i++ ) {    // 未設定の場合は、長さゼロの配列。
566                                                        if( reqAttClmsNo[i] >= 0 ) {
567                                                                final String reqAtt = (String)getRequestAttribute( reqAttClms[i] );
568                                                                rowData[reqAttClmsNo[i]] = reqAtt;              // 同時に戻すカラムの値
569                                                        }
570                                                }
571                                        }
572                                }
573
574                                final String val = rowData[vcNo] ;
575
576                                buf.append( getMapVals( rowData , val , isNormal , cls , tips , null ) );
577                        }
578                        else {
579                                buf.append( super.getRequestValue( key , xssCheck ) );                          // 添字も合わせて、上位に問い合わせる。
580                        }
581                }
582                else {
583                        final String subKey = mapkey.substring( 0,mapkey.length()-type );               // ほんとは、keyBuf で処理したかった。
584                        final List<String> list = new ArrayList<>();
585                        for( final Map.Entry<String,String[]> entry : mapObj.entrySet() ) {             // {@XXXX}を見つける都度、全Mapをスキャンしているので、非効率
586                                final String mapkey2 = entry.getKey();
587                                // 6.8.0.1 (2017/06/30) str指定のキーワード
588                                if( mapkey2.startsWith( subKey ) && ( instr == null || mapkey2.contains( instr ) ) ) {
589                                        final String[] rowData = entry.getValue();
590                                        if( rowData != null && vcNo < rowData.length ) {
591                                                final String sufix = isSufix ? mapkey2.substring( subKey.length() ) : null ;    // あいまいキーの残りの文字列
592                                                // isAttVal == true のときは、RequestAttribute から取り出すが、キーは、大文字になっているので注意。
593                                                // 6.9.2.0 (2018/03/05) DBTableModel への値のフィードバック
594//                                              final String val   = isAttVal ? nval( (String)getRequestAttribute( mapkey2.toUpperCase(Locale.JAPAN) ) , rowData[vcNo] ) : rowData[vcNo] ;
595                                                if( isAttVal ) {
596                                                        // isAttVal == true のときは、RequestAttribute から取り出すが、キーは、大文字になっているので注意。
597                                                        final String attVal = (String)getRequestAttribute( mapkey.toUpperCase(Locale.JAPAN) );
598                                                        if( attVal != null && !attVal.isEmpty() ) {
599                                                                rowData[vcNo] = attVal; // RequestAttribute をテーブルに戻す。
600                                                                for( int i=0; i<reqAttClmsNo.length; i++ ) {    // 未設定の場合は、長さゼロの配列。
601                                                                        if( reqAttClmsNo[i] >= 0 ) {
602                                                                                final String reqAtt = (String)getRequestAttribute( reqAttClms[i] );
603                                                                                rowData[reqAttClmsNo[i]] = reqAtt;              // 同時に戻すカラムの値
604                                                                        }
605                                                                }
606                                                        }
607                                                }
608
609                                                final String val = rowData[vcNo] ;
610
611                                                list.add( getMapVals( rowData , val , isNormal , cls , tips , sufix ) );
612                                                restMap.remove( mapkey2 );                      // 6.7.8.0 (2017/04/21) valueMapParam関連
613                                        }
614                                }
615                        }
616                        if( type == 2 ) { Collections.reverse( list ); }        // 逆順
617                        list.forEach( v -> buf.append( v ) );
618                }
619
620                return buf.toString();
621        }
622
623        /**
624         * 指定の文字列バッファから、キーワードのパラメータを取り出します。
625         *
626         * 元の文字列バッファは、そのキーワード部分を削除し、パラメータの値は、RETURNで
627         * 返します。存在しない場合は、null を返します。
628         *
629         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
630         *
631         * @param       keyBuf 文字列バッファ
632         * @param       key キーワード(cls,tips,str)
633         * @return      パラメータ
634         */
635        private String getExtParam( final StringBuilder keyBuf , final String key ) {
636                // key="XXX" 属性の取得
637                String rtn = null;
638                final int ad2 = keyBuf.lastIndexOf( key );                              // = の前後にスペースは入れてはいけない。
639                if( ad2 > 0 ) {
640                        final int st = ad2 + key.length() ;
641                        // cls="B" や、cls='C' のように、文字列指定されているはずなので、その中身を削除します。
642                        final String qot = keyBuf.substring( st,st+1 );         // 1文字取り出す。
643                        final int    ed  = keyBuf.indexOf(  qot,st+1 );         // 対になる後ろのクオートの位置を見つける。
644                        if( ed >= 0 ) {
645                                rtn = keyBuf.substring( st+1 , ed );                    // 前後のクオートは、含まない。
646                                keyBuf.delete( ad2 , ed+1 ) ;                                   // 間を抜く
647                        }
648                        else {
649                                // 文法間違い。どうするか?
650                                System.err.println( "指定の文法が間違っています。key=" + key );
651                        }
652                }
653
654                return rtn;
655        }
656
657        /**
658         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を処理の対象とします。
659         *
660         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
661         *
662         * @return      選択行の配列
663         * @og.rtnNotNull
664         */
665        @Override
666        protected int[] getParameterRows() {
667                final int[] rowNo ;
668                if( selectedAll ) {
669                        final int rowCnt = table.getRowCount();
670                        rowNo = new int[ rowCnt ];
671                        for( int i=0; i<rowCnt; i++ ) {
672                                rowNo[i] = i;
673                        }
674                } else {
675                        rowNo = super.getParameterRows();
676                }
677                return rowNo ;
678        }
679
680        /**
681         * ValueMapParamTagで設定された各種パラメータ を受け取ります。
682         *
683         * @og.rev 6.7.8.0 (2017/04/21) ValueMapParamTag のパラメータを追加します。
684         *
685         * @param       pMap ValueMapParamTagで設定された各種パラメータ
686         */
687        protected void setParam( final ConcurrentMap<VMP_KEYS,String> pMap ) {
688                paramMap.putAll( pMap );                                // ValueMapParamTag と分けるために、内容をコピーします。
689
690                restChangeKey = paramMap.get( VMP_KEYS.REST_CHANGE_KEY );
691        }
692
693        /**
694         * ValueMapParamTagで設定された各種パラメータを元に、残カラムを処理します。
695         *
696         * 処理としては、{&#064;XXXX} の XXXX 部分を、valueMap の未使用キーに変換します。
697         * その後、通常のパラメータ処理を行います。
698         * REST_MARK_CLM が指定されている場合は、DBTableModel に対して、マーク処理を行います。
699         *
700         * @og.rev 6.7.8.0 (2017/04/21) ValueMapParamTag のパラメータを追加します。
701         * @og.rev 6.9.9.0 (2018/08/20) YYYYキーワードに置換するグループカラム名
702         * @og.rev 6.9.9.2 (2018/09/18) YYYYキーワードで、@のあるなしを対処しておきます(前方一致の必要性)。
703         * @og.rev 7.4.2.3 (2021/06/09) グループ処理で、未処理データが使用済みになった。
704         *
705         * @return      残カラム処理の結果
706         */
707        private String makeRestValue() {
708                // 先にDBTableModel に対して、マーク処理を行います。
709                // パラメータ処理を行うと、キーワードによっては、restMap から値が削除されるためです。
710                final String markClm = paramMap.get( VMP_KEYS.REST_MARK_CLM );
711                final String markVal = paramMap.get( VMP_KEYS.REST_MARK_VAL );
712
713                if( !restMap.isEmpty() && markClm != null && markVal != null ) {
714                        final int   clmNo = table.getColumnNo( markClm , false );       // ifの階層が深くなるのが嫌なので、まとめてチェックします。
715                        final int[] rowNo = getParameterRows();
716                        if( clmNo >=0 && rowNo.length > 0 ) {
717                                final int[] keysNo = getClmNos( table,keys , 0 );               // keysが指定されない場合は、先頭カラムを使用します。
718
719                                for( int row=0; row<rowNo.length; row++ ) {
720                                        final String[] rowData = table.getValues( row );
721
722                                        // Map に登録されているキーを連結して作成します。
723                                        final String mapkey = getAppendKeys( rowData , keysNo , separator );
724
725                                        if( restMap.containsKey( mapkey ) ) {                           // 残っている場合
726                                                table.setValueAt( markVal , row , clmNo );
727                                        }
728                                }
729                        }
730                }
731
732                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
733                if( restMap.isEmpty() ) {
734                        // restMap の残り処理が必要かどうか。restMap が空で、NONE_CLS_KEY が存在する場合は、残を隠す。
735                        useNoneClsKey = paramMap.containsKey( VMP_KEYS.NONE_CLS_KEY );
736                }
737                else {
738                        final String changeVal = paramMap.get( VMP_KEYS.BODY_VAL );
739                        // パラメータ処理を行うと、キーワードによっては、restMap から値が削除されるためです。
740                        final String[] restKeys = restMap.keySet().toArray( new String[restMap.size()] );
741
742                        final String grpClm   = paramMap.get( VMP_KEYS.GRP_KEY_CLM );           // 6.9.9.0 (2018/08/20)
743                        final int    grpClmNo = table.getColumnNo( grpClm , false );            // 6.9.9.0 (2018/08/20) 無ければ、-1
744
745                        // 6.9.9.0 (2018/08/20) YYYYキーワードに置換するグループカラム名 対応
746                        for( final String key : restKeys ) {
747                                final String repStr = changeVal.replaceAll( "XXXX" , key );
748
749                                if( grpClmNo < 0 ) {
750                                        buf.append( getRequestParameter( repStr ) );                            // 従来どおり、YYYY 処理を行わない。
751                                }
752                                else {
753                                        final String[] vals = restMap.get( key );
754        //                              if( vals == null ) { continue; }                                                        // グループ処理で、未処理データが使用済みになった。
755                                        if( vals == null || vals.length == 0 ) { continue; }            // 7.4.2.3 (2021/06/09) グループ処理で、未処理データが使用済みになった。
756                                        final String grpKey = vals[grpClmNo];
757                                        final String repStr2 = repStr.replaceAll( "YYYY" , grpKey );
758                                        buf.append( getRequestParameter( repStr2 ) );
759                                }
760                        }
761
762//                      // 6.9.9.0 (2018/08/20)
763//                      for( final String key : restKeys ) {
764//                              final String repStr = changeVal.replaceAll( "XXXX" , key );
765//                              buf.append( getRequestParameter( repStr ) );
766//                      }
767                }
768
769                return buf.toString();
770        }
771
772        /**
773         * 【TAG】データを全件選択済みとして処理するかどうか[true/false]を指定します(初期値:false)。
774         *
775         * @og.tag
776         * 全てのデータを選択済みデータとして扱って処理します。
777         * 全件処理する場合に、(true/false)を指定します。
778         * 初期値は false です。
779         *
780         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
781         *
782         * @param  all 選択済み処理可否 [true:全件選択済み/false:通常]
783         */
784        public void setSelectedAll( final String all ) {
785                selectedAll = nval( getRequestParameter( all ),selectedAll );
786        }
787
788        /**
789         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
790         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
791         *
792         * @og.tag
793         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
794         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
795         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
796         * この tableId 属性を利用して、メモリ空間を分けます。
797         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value HybsSystem#TBL_MDL_KEY}])。
798         *
799         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
800         *
801         * @param       id テーブルID (sessionに登録する時のID)
802         */
803        public void setTableId( final String id ) {
804                tableId   = nval( getRequestParameter( id ),tableId );
805        }
806
807        /**
808         * 【TAG】パラメータ に登録するキーをセットします。
809         *
810         * @og.tag keysが指定されない場合は、先頭カラムを使用します。
811         *
812         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
813         *
814         * @param       key1 登録キー
815         */
816        public void setKeys( final String key1 ) {
817                keys = nval( getRequestParameter( key1 ),keys ) ;
818        }
819
820        /**
821         * 【TAG】パラメータ から取り出す値カラムを指定ます。
822         *
823         * @og.tag valClmが指定されない場合は、2番目のカラムを使用します。
824         *
825         * @og.rev 6.7.2.0 (2017/01/16) 新規作成
826         *
827         * @param       clm 取り出す値カラム
828         */
829        public void setValClm( final String clm ) {
830                valClm = nval( getRequestParameter( clm ),valClm ) ;
831        }
832
833        /**
834         * 【TAG】パラメータが NULL の時に、設定しないカラム名を、CSV形式で指定します。
835         *
836         * @og.tag
837         *  nnClms属性:この属性で指定された値が、nullの場合、{&#064;XXXX} の解析を行いません。
838         *  正確に言うと、Mapに取り込みません。この場合、先のholdTag属性で指定したタグそのものも
839         *  出力しません。
840         *
841         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
842         * @og.rev 6.7.2.0 (2017/01/16) 名称変更
843         *
844         * @param       clms NULL の時に、設定しないカラム名を、CSV形式で指定
845         */
846        public void setNnClms( final String clms ) {
847                nnClms = nval( getRequestParameter( clms ),nnClms );
848        }
849
850        /**
851         * 【TAG】値の前後を、挟むタグを指定します。
852         *
853         * @og.tag
854         *  holdTag属性:{&#064;XXXX} を、指定のタグで囲います。
855         *  例えば、holdTag="span" とすると、&lt;span class="YYYYの値" &gt;XXXXの値&lt;/span&gt;
856         *  という文字列を作成します。
857         *  clsClms 属性や、{&#064;XXXX cls="B"} を使用する場合は、holdTag 属性の指定が必要です。
858         *
859         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
860         *
861         * @param       tag 値の前後を挟むタグ
862         * @see         #setClsClms( String )
863         */
864        public void setHoldTag( final String tag ) {
865                holdTag = nval( getRequestParameter( tag ),holdTag );
866        }
867
868        /**
869         * 【TAG】holdTagを使用するとき、そのタグの属性にclass属性を出力する場合のカラム名をCSV形式で指定します。
870         *
871         * @og.tag
872         *   clsClms属性:先の指定のタグで囲う場合、そのタグのclass属性を指定できます。
873         *   複数指定した場合は、スペースで、連結します。
874         *   一括指定ではなく、個別に指定する場合は、{&#064;XXXX cls="B"} 構文を使用します。
875         *   holdTag属性が設定されていない場合は、どちらも無視されます。
876         *
877         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
878         * @og.rev 6.7.2.0 (2017/01/16) 名称変更
879         *
880         * @param       clms class属性を出力する場合のカラム名を、CSV形式で指定
881         * @see         #setHoldTag( String )
882         */
883        public void setClsClms( final String clms ) {
884                clsClms = nval( getRequestParameter( clms ),clsClms );
885        }
886
887        /**
888         * 【TAG】holdTagを使用するとき、そのタグの属性にtitle属性を出力する場合のカラム名をCSV形式で指定します。
889         *
890         * @og.tag
891         *   tipsClms属性:先の指定のタグで囲う場合、そのタグのtitle属性を指定できます。
892         *   複数指定した場合は、スペースで、連結します。
893         *   一括指定ではなく、個別に指定する場合は、{&#064;XXXX tips="YYYY"} 構文を使用します。
894         *   holdTag属性が設定されていない場合は、どちらも無視されます。
895         *
896         * @og.rev 6.7.3.0 (2017/01/16) 名称変更
897         *
898         * @param       clms title属性を出力する場合のカラム名を、CSV形式で指定
899         * @see         #setHoldTag( String )
900         */
901        public void setTipsClms( final String clms ) {
902                tipsClms = nval( getRequestParameter( clms ),tipsClms );
903        }
904
905        /**
906         * 【TAG】{&#064;^XXXX}使用時に request.getAttribute() をセットすると同時に設定するカラム名をCSV形式で指定します。
907         *
908         * @og.tag
909         * DBTableModel と別に、value等で設定した値を、{&#064;^XXXX} で置き換えますが、
910         * その際、DBTableModel のデータも置き換えないと、表示とデータ(例えば、一覧表示やファイル出力時)が異なります。
911         * また、置き換えるに当たって、他の項目(カラム)も置き換えないと、矛盾が生じる恐れがあります。
912         * そこで、request.getAttribute() をセットする場合に、同時にセットするカラムを指定することで、
913         * 同時に値の書き換えを行います。
914         * なお、値は、request.getAttribute() で取得します。
915         *
916         * @og.rev 6.9.2.0 (2018/03/05) 新規作成
917         *
918         * @param       clms request.getAttribute()使用時に、同時に置き換えるカラム名を、CSV形式で指定
919         */
920        public void setReqAttUpClms( final String clms ) {
921                reqAttUpClms = nval( getRequestParameter( clms ),reqAttUpClms );
922        }
923
924        /**
925         * 【TAG】キーとなるカラム名の値を連結する項目区切り文字をセットします(初期値:"_")。
926         *
927         * @og.tag
928         * keysで、複数のキーの値を連結して、Mapのキーにしますが、そのときの連結文字列を指定します。
929         * 初期値は、"_" に設定されています。
930         *
931         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
932         *
933         * @param   sepa 連結文字列 (初期値:"_")
934         */
935        public void setSeparator( final String sepa ) {
936                separator = nval( getRequestParameter( sepa ),separator );
937        }
938
939        /**
940         * 【TAG】パラメータの HTMLTag開始/終了文字(&gt;&lt;) 存在チェックを実施するかどうか[true/false]を設定します
941         *              (初期値:USE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
942         *
943         * @og.tag
944         * クロスサイトスクリプティング(XSS)対策の一環としてless/greater than signについてのチェックを行います。
945         * (&gt;&lt;) が含まれていたエラーにする(true)/かノーチェックか(false)を指定します。
946         * (初期値:システム定数のUSE_XSS_CHECK[={@og.value SystemData#USE_XSS_CHECK}])。
947         *
948         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
949         *
950         * @param       flag    XSSチェック [true:する/false:しない]
951         * @see         org.opengion.hayabusa.common.SystemData#USE_XSS_CHECK
952         */
953        public void setXssCheck( final String flag ) {
954                xssCheck = nval( getRequestParameter( flag ),xssCheck );
955        }
956
957        /**
958         * このオブジェクトの文字列表現を返します。
959         * 基本的にデバッグ目的に使用します。
960         *
961         * @og.rev 6.7.1.0 (2017/01/05) 新規作成
962         *
963         * @return このクラスの文字列表現
964         * @og.rtnNotNull
965         */
966        @Override
967        public String toString() {
968                return ToString.title( this.getClass().getName() )
969                                .println( "VERSION"                     ,VERSION                )
970                                .println( "tableId"                     ,tableId                )
971                                .println( "selectedAll"         ,selectedAll    )
972                                .println( "keys"                        ,keys                   )
973                                .println( "holdTag"                     ,holdTag                )
974                                .println( "clsClms"                     ,clsClms                )
975                                .println( "tipsClms"            ,tipsClms               )
976                                .println( "valClm"                      ,valClm                 )
977                                .println( "nnClms"                      ,nnClms                 )
978                                .println( "scope"                       ,scope                  )
979                                .println( "separator"           ,separator              )
980                                .println( "xssCheck"            ,xssCheck               )
981                                .println( "Other..."    ,getAttributes().getAttribute() )
982                                .fixForm().toString() ;
983        }
984}