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