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.plugin.view;
017
018import org.opengion.fukurou.util.StringUtil;
019import org.opengion.fukurou.util.Attributes;
020
021import org.opengion.hayabusa.db.DBColumn;
022import org.opengion.hayabusa.db.DBColumnConfig;
023import org.opengion.hayabusa.db.DBTableModel;
024import org.opengion.hayabusa.resource.ResourceFactory;
025import org.opengion.hayabusa.resource.ResourceManager;
026import org.opengion.hayabusa.resource.LabelData;
027import org.opengion.hayabusa.common.HybsSystemException;
028
029import java.time.LocalDateTime;                                                                 // 7.4.2.1 (2021/05/21)
030import java.time.format.DateTimeFormatter;                                              // 7.4.2.1 (2021/05/21)
031
032/**
033 * 指定の行-列と、動的カラムのテーブルを作成するクラスです。
034 *
035 * AbstractViewForm により、setter/getterメソッドのデフォルト実装を提供しています。
036 * 各HTMLのタグに必要な setter/getterメソッドのみ,追加定義しています。
037 * 行単位の繰り返し色は使いません。
038 *
039 * RENDERER,EDITOR,DBTYPE を使用する代わりに、簡易的な DATA_TYPE で決定します。
040 *
041 * GG10 履歴テーブル (書き込むテーブル)
042 *   トークン(TOKEN)                        必須キー(トークン)
043 *   更新カウンタ(UPCNT)              ← 未使用(同一トークンでの最大値が有効:逆順検索なので、最初に見つかった値を採用)
044 *   値データ(VAL)                 必須キー(値の設定)
045 *   単位(TANI)                           フィールドの後ろに追記(連続の場合は、一番最後のみ)
046 *   判定結果(JUDG)                 0:未決 1:不要 2:任意 3:合格 4:保留 5:警告 6:必須 7:不合格
047 *   判定理由(RIYU)                 上限、下限で判定した結果が入っている。titleに入れてポップアップさせる
048 *
049 * GG01 トークンマスタ (GG02がnullの時)
050 *   トークン名称(TKN_NM)             ← 未使用(GG02 のトークン名称が未設定の場合…SQL文で処理済み)
051 *   表示桁数(VIEW_LEN)             テキストフィールドの長さセット
052 *   データ型(DATA_TYPE)           EDITORを決定
053 *   トークングループ(TKN_GRP)        (未使用=GG03のSEL_KEY の条件に使用している)
054 *
055 * GG03 選択マスタ (GG01 トークングループの名称とマーカー)
056 *   選択名称(SEL_NM)               トークングループ名で、fieldset のキーブレイクに使用
057 *   マーカー(MARKER)                       fieldset のstyle属性として使用
058 *
059 * GG02 雛形設定マスタ
060 *   トークン名称(TKN_NM)             (名称優先)
061 *   タブ名称(TAB_NM)              ← 未使用
062 *   タグループ配置(GRP_POS) ← 未使用(fieldset を配置する場合に使う予定)
063 *   初期値(DEF_VAL)               値の設定 する場合の初期値に使用
064 *   行番号(ROWNO)                 トークンの並び順を指定
065 *   列番号(COLNO)                 ← 未使用(トークンの並び順のサブ)
066 *   行数(ROWSPAN)                        tableのカラムを複数縦につなげる場合
067 *   列数(COLSPAN)                        tableのカラムを複数横につなげる場合
068 *   登録方法(CDREC)                0:未決 1:不要 2:任意 4:保留 6:必須
069 *   表示方法(CDDISP)               1:ラベルカラム 2:カラム単発 3:カラム連続 4:表示のみ
070 *   異常下限(E_MIN)                異常値の下限判定をフィールドの横に記述
071 *   警告下限(W_MIN)                警告値の下限判定をフィールドの横に記述
072 *   警告上限(W_MAX)                警告値の上限判定をフィールドの横に記述
073 *   異常上限(E_MAX)                異常値の上限判定をフィールドの横に記述
074 *   オプション属性(OPT_ATTR) トークンのフォームの属性に追記する
075 *
076 * @og.group 画面表示
077 *
078 * @version  7.3
079 * @author   Kazuhiko Hasegawa
080 * @since    JDK11.0,
081 */
082public class ViewForm_HTMLTokenTable extends ViewForm_HTMLTable {
083        /** このプログラムのVERSION文字列を設定します。   {@value} */
084        private static final String VERSION = "7.4.2.3 (2021/06/09)" ;
085
086        private static final String INS_VAL = "$$$$";           // 表示方法(CDDISP)が3:カラム連続 の場合の挿入位置を示すマーカー
087
088        private static final DateTimeFormatter YMD    = DateTimeFormatter.ofPattern( "yyyyMMdd" );
089        private static final DateTimeFormatter HM     = DateTimeFormatter.ofPattern( "HHmm" );
090        private static final DateTimeFormatter YMDHMS = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss" );
091
092        // Rendereを使う データ型(DATA_TYPE) の指定
093        // TP_IFRAME,TP_IMAGE,TP_LINE,TP_SIGNALは、Rendereを使う。contains で判定しているので、部分一致に注意
094        private static final String RENDERE_TYPE = "TP_IFRAME,TP_IMAGE,TP_LINE,TP_SIGNAL" ;
095
096        private int noToken             = -1;
097//      private int noUpcnt             = -1;
098        private int noVal               = -1;
099        private int noTani              = -1;
100        private int noJudg              = -1;
101        private int noRiyu              = -1;
102
103        private int noViewlen   = -1;
104        private int noType              = -1;
105
106        private int noSelnm             = -1;
107        private int noMarker    = -1;
108
109        private int noName              = -1;
110        private int noDefval    = -1;
111        private int noRowno             = -1;
112//      private int noColno             = -1;
113        private int noRowspan   = -1;
114        private int noColspan   = -1;
115
116        private int noCdrec             = -1;
117        private int noCddisp    = -1;
118        private int noEmin              = -1;
119        private int noWmin              = -1;
120        private int noWmax              = -1;
121        private int noEmax              = -1;
122        private int noOptAttr   = -1;
123
124        private DBTableModel table ;
125
126        /**
127         * デフォルトコンストラクター
128         *
129         * @og.rev 7.3.2.1 (2021/03/25) 動的カラムのテーブルを作成する
130         */
131        public ViewForm_HTMLTokenTable() { super(); }           // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
132
133        /**
134         * 初期化します。
135         * ここでは、内部で使用されているキャッシュをクリアし、
136         * 新しいモデル(DBTableModel)と言語(lang) を元に内部データを再構築します。
137         * ただし、設定情報は、以前の状態がそのままキープされています。
138         *
139         * @og.rev 7.3.2.1 (2021/03/25) 動的カラムのテーブルを作成する
140         *
141         * @param       table   DBTableModelオブジェクト
142         */
143        @Override
144        public void init( final DBTableModel table ) {
145                super.init( table );
146                this.table = table;
147
148                noToken         = table.getColumnNo( "TOKEN"                    );      // トークン
149        //      noUpcnt         = table.getColumnNo( "UPCNT"                    );      // 更新カウンタ
150                noVal           = table.getColumnNo( "VAL"                              );      // 値
151                noTani          = table.getColumnNo( "TANI"             , false );      //
152                noJudg          = table.getColumnNo( "JUDG"             , false );      //
153                noRiyu          = table.getColumnNo( "RIYU"             , false );      //
154                noViewlen       = table.getColumnNo( "VIEW_LEN" , false );      //
155                noType          = table.getColumnNo( "DATA_TYPE", false );      //
156                noSelnm         = table.getColumnNo( "SEL_NM"   , false );      //
157                noMarker        = table.getColumnNo( "MARKER"   , false );      //
158                noName          = table.getColumnNo( "TKN_NM"   , false );      //
159                noDefval        = table.getColumnNo( "DEF_VAL"  , false );      //
160                noRowno         = table.getColumnNo( "ROWNO"    , false );      //
161        //      noColno         = table.getColumnNo( "COLNO"    , false );      //
162                noRowspan       = table.getColumnNo( "ROWSPAN"  , false );      //
163                noColspan       = table.getColumnNo( "COLSPAN"  , false );      //
164                noCdrec         = table.getColumnNo( "CDREC"    , false );      //
165                noCddisp        = table.getColumnNo( "CDDISP"   , false );      //
166                noEmin          = table.getColumnNo( "E_MIN"    , false );      //
167                noWmin          = table.getColumnNo( "W_MIN"    , false );      //
168                noWmax          = table.getColumnNo( "W_MAX"    , false );      //
169                noEmax          = table.getColumnNo( "E_MAX"    , false );      //
170                noOptAttr       = table.getColumnNo( "OPT_ATTR" , false );      //
171        }
172
173        /**
174         * DBTableModel から HTML文字列を作成して返します。
175         * startNo(表示開始位置)から、pageSize(表示件数)までのView文字列を作成します。
176         * 表示残りデータが pageSize 以下の場合は,残りのデータをすべて出力します。
177         *
178         * @og.rev 7.3.2.1 (2021/03/25) 動的カラムのテーブルを作成する
179         * @og.rev 7.4.2.1 (2021/05/21) TP_RADIO追加,4:表示のみは、値をRendererで出す。
180         * @og.rev 7.4.2.3 (2021/06/09) 数値フォーマット(カンマ無し、桁数指定)を設定、addNoValue制御を追加
181         *
182         * @param  startNo    表示開始位置
183         * @param  pageSize   表示件数
184         *
185         * @return  DBTableModelから作成された HTML文字列
186         * @og.rtnNotNull
187         */
188        @Override
189        public String create( final int startNo, final int pageSize )  {
190                if( getRowCount() == 0 ) { return ""; } // 暫定処置
191
192                final int lastNo = getLastNo( startNo, pageSize );
193
194                final StringBuilder tagOut = new StringBuilder( BUFFER_LARGE );         // 最終的に出力するタグの文字列バッファ
195//                      .append( getCountForm( startNo,pageSize ) )
196//                      .append( getHeader() );
197
198                final ResourceManager resource = ResourceFactory.newInstance( "ja" ) ;
199
200                int lastRow  = -1;
201//              int bgClrCnt = 0;
202
203                String bkSelNm = "";                    // 最後にセットされた SEL_NM (キーブレイク用)
204                String bkToken = "";                    // 更新カウンタの最大値(逆順検索しているので、最初に見つかったトークン)のみ採用する。
205
206                final int clmCnt = getColumnCount();
207                for( int row=startNo; row<lastNo; row++ ) {
208                        final int rowno = rowData( row,noRowno,row-startNo );   // DBに指定した列の番号
209
210                        final String token = table.getValue( row,noToken );
211        //              final String upcnt = table.getValue( row,noUpcnt );             // 更新カウンタは、同一トークン内で、一番最初に現れたレコードのみ使用する。
212                        if( bkToken.equals( token ) ) { continue; }                             // 同一トークンなら、過去データなので取り直し
213                        bkToken = token;
214
215                        final String selNm  = rowData( row,noSelnm ,"" );               // 選択名称(SEL_NM)
216//                      if( lastRow != rowno || !bkSelNm.equals( selNm ) ) {
217                        if( lastRow != rowno ) {        // 行のブレイク
218                                if( lastRow >= 0 ) {    // 最初のループだけは、実行しない。
219                                        if( !bkSelNm.equals( selNm ) && !bkSelNm.isEmpty() ) {
220                                                tagOut.append( "</tr></tbody></table></fieldset>" );
221                                        }
222                                        else {
223                                                tagOut.append(" </tr>").append( CR );
224                                        }
225                                }
226
227                                // ※ 繰り返し色:getBgColorCycleClass( bgClrCnt++,row ) は使いません。
228                                if( !bkSelNm.equals( selNm ) && !selNm.isEmpty() ) {
229                                        final String style = rowData( row,noMarker," style=\"", "\"" ); // マーカー(MARKER) は、style属性に追加する。
230                                        tagOut.append( "<fieldset" ).append( style ).append( "><legend>" ).append( selNm ).append( "</legend>" )
231                                                .append("<table><tbody><tr>").append( CR );             // 繰り返し色背景色は使わない
232//                                              .append("<table><tbody><tr").append( getBgColorCycleClass( bgClrCnt++,row ) ).append('>').append( CR );
233                                }
234                                else if( lastRow < 0 ) {        // 最初の行で、fieldset のラベルが 未指定の場合、<table> が必要。
235                                        tagOut.append("<table><tbody><tr>").append( CR );       // 繰り返し色背景色は使わない
236//                                      tagOut.append("<table><tbody><tr").append( getBgColorCycleClass( bgClrCnt++,row ) ).append('>').append( CR );
237                                }
238                                else {
239                                        tagOut.append("<tr>").append( CR );                                     // 繰り返し色背景色は使わない
240//                                      tagOut.append("<tr").append( getBgColorCycleClass( bgClrCnt++,row ) ).append('>').append( CR );
241                                }
242                                bkSelNm = selNm;
243                                lastRow = rowno;
244                        }
245
246                        final String lbl   = rowData( row,noName,token );               // 表示するラベル(無ければトークンそのもの)
247                        final String dbType= rowData( row,noType,"TP_TEXT" );   // DBタイプ。未指定時は、TP_TEXT:テキスト
248
249                        final DBColumn clm = resource.makeDBColumn( dbType,lbl );
250                        final DBColumnConfig config = clm.getConfig();
251                        config.setName( "VAL" );                                                                // 登録するカラムの名前は、VAL になる。
252
253                        final String defVal = timeVal( dbType,rowData( row,noDefval,"" ) );     // 初期値  7.4.2.1 (2021/05/21)
254                        final String val = rowData( row,noVal,defVal );
255
256                        final String cdrec = rowData( row,noCdrec ,"0" );       // 0:未決 1:不要 2:任意        4:保留        6:必須
257                        final String judg  = rowData( row,noJudg  ,"0" );       // 0:未決 1:不要 2:任意 3:合格 4:保留 5:警告 6:必須 7:不合格
258
259                        final Attributes editAttri = config.getEditorAttributes();              // 内部オブジェクトを取得している。
260//                      final String optAttr = changeOptAttr( editAttri, rowData( row,noOptAttr,"" ) ); // オプション属性(OPT_ATTR)
261                        final String optAttr = changeOptAttr( config, editAttri, rowData( row,noOptAttr,"" ) ); // 7.4.2.3 (2021/06/09) オプション属性(OPT_ATTR)
262
263                        final boolean isEmp = val.isEmpty()                                                                                                     // 本当にempty
264                                                                        || ( "TP_CHECK".equals( dbType ) && "0".equals(val) );          // TP_CHECK が '0' は、未設定と同じ
265
266                        if( isEmp ) {                                                                           // val が未設定の場合は、cdrec をそのままセットする。
267                                editAttri.add( "class" , "judg" + cdrec );              // 注意:キーは、judg
268                        }
269                        else {                                                                                          // val が設定済みの場合は、judg をセットする。
270                                editAttri.add( "class" , "judg" + judg );
271                        }
272
273        //              日付フォーマットが日本風じゃないので、取りやめ
274        //              // dbType が、TP_YMD,TP_HMS,TP_YMDH の場合は、日付、時刻のみ可能とする。
275        //              if( "TP_YMD".equals( dbType ) ) {
276        //                      editAttri.set( "type" , "date" );                               // type="date" で日付
277        //              }
278        //              else if( "TP_HMS".equals( dbType ) ) {
279        //                      editAttri.set( "type" , "time" );                               // type="time" だけだと、秒が入らない。
280        //                      editAttri.set( "step" , "1" );                                  // 秒 を有効にするには、step="1" にする。
281        //              }
282
283                        // dbType が、TP_INT の場合は、数値のみ可能とする。
284                        if( "TP_INT".equals( dbType ) ) {
285                                editAttri.set( "type" , "number" );             // type="number" だけだと、整数しか入らない。
286                                // Editor_NUMBER は、フォーマット指定できない。TableFilter_JUDG で書き戻しして、カンマを削除している。
287                        }
288                        else if( "TP_REAL".equals( dbType ) ) {
289                                editAttri.set( "type" , "number" );             // type="number" だけだと、整数しか入らない。
290                                editAttri.set( "step" , "0.01"   );             // とりあえず、無条件に、0.01 刻みにする。
291                                config.setEditorParam( "#0.00" );               // 7.4.2.3 (2021/06/09) 数値フォーマット(カンマ無し、桁数指定)を設定
292                        }
293
294        //              if( noViewlen >= 0 ) { config.setFieldSize( table.getValue( row,noViewlen ) ); }        // フィールドの表示桁数
295                        final String viewLen = rowData( row,noViewlen,"" );             // フィールドの文字桁数
296                        if( !viewLen.isEmpty() ) {
297                                config.setMaxlength( viewLen );
298
299                                // dbType が、TP_REAL の場合は、表示桁数 に合わせて、step を再設定する。
300                                if( "TP_REAL".equals( dbType ) ) {
301                                        if(              viewLen.contains( ".1" ) ) {
302                                                editAttri.set( "step" , "0.1" );
303        //                                      config.setEditorParam( "#0.0" );                        // 7.4.2.3 (2021/06/09)  数値フォーマット(カンマ無し、桁数指定)を設定
304                                        }
305                                        else if( viewLen.contains( ".2" ) ) {
306                                                editAttri.set( "step" , "0.01" );
307        //                                      config.setEditorParam( "#0.00" );                       // 7.4.2.3 (2021/06/09)  数値フォーマット(カンマ無し、桁数指定)を設定
308                                        }
309                                        else if( viewLen.contains( ".3" ) ) {
310                                                editAttri.set( "step" , "0.001" );
311        //                                      config.setEditorParam( "#0.000" );                      // 7.4.2.3 (2021/06/09)  数値フォーマット(カンマ無し、桁数指定)を設定
312                                        }
313                                        else {
314                                                editAttri.set( "step" , "any" );
315                                        }
316                                }
317                        }
318
319                        final String cddisp = rowData( row,noCddisp,"1" );              // 1:ラベルカラム 2:カラム単発 3:カラム連続 4:表示のみ
320
321                        // 表示方法(CDDISP)=3:カラム連続 かつ ラベルの末尾が数字の場合は、①~の番号を placeholder にセットする。
322                        if( "3".equals( cddisp ) && !optAttr.contains( "placeholder" ) ) {                      // オプション属性 に plaseholder が未設定の場合のみ
323                                int idx = lbl.length()-1;
324                                while( idx >= 0 ) {
325                                        final char ch = lbl.charAt( idx );
326                                        if( '0' <= ch && ch <= '9' ) { idx--; }         // 末尾から数字の間、処理を継続
327                                        else { break; }                                                         // 数字でなくなった時点で処理終了
328                                }
329                                if( idx >= 0 && idx < lbl.length()-1 ) {                // 末尾に何らかの数値があった場合
330                                        final char plc = (char)('①' + ( Integer.parseInt(lbl.substring( idx+1 ))-1 ) );
331                                        editAttri.set( "placeholder" , String.valueOf( plc ) );
332                                }
333                        }
334
335//                      // 理由 があれば、title に入れて popup させる
336//                      final String riyu= rowData( row,noRiyu,"" );
337//                      if( ! riyu.isEmpty() ) {
338//                              editAttri.set( "title" , riyu );
339//                      }
340
341                        // 登録方法(CDREC) が、1:不要 か、書き込み禁止(isWritable( row )がfalse) の場合は、disabled をセットする。
342                        if( "1".equals( cdrec ) || !isWritable( row ) ) {
343                                editAttri.set( "disabled" , "disabled" );               // 1:不要 は、disabled をセットする。
344                        }
345
346        //              config.setEditorAttributes( editAttri );                        // 内部オブジェクトを追加しているので、再設定不要。追加する場合に使用する。
347
348                        if( "TP_FILE".equals( dbType ) ) {                                      // ファイル添付は、値をアップロードするフォルダとする。
349                                config.setEditorParam( "UPLOAD_DIR=" + val );   // jsp 以下のフォルダになります。
350                        }
351
352                        // RENDERE_TYPE="TP_IFRAME,TP_IMAGE,TP_LINE,TP_SIGNAL" は、Rendereを使う。
353                        // 表示方法(CDDISP) 4:表示のみ の場合も、Rendereを使う。
354        //              final String valBuf ;
355                        String valBuf ;
356//                      if( "TP_IFRAME".equals( dbType ) || "TP_IMAGE".equals( dbType ) || "TP_LINE".equals( dbType ) || "TP_SIGNAL".equals( dbType ) || "4".equals( cddisp ) ) {
357                        if( RENDERE_TYPE.contains( dbType ) || "4".equals( cddisp ) ) {
358                                if( !optAttr.isEmpty() ) {
359        //                              final Attributes rendAttri = config.getRendererAttributes();    // 内部オブジェクトを取得している。
360        //                              rendAttri.add( "optionAttributes" , optAttr );
361                                        config.setRendererParam( optAttr );
362                                }
363                                valBuf = new DBColumn( config ).getRendererValue( row,val );            // 表示欄(Renderer)
364                        }
365                        else {
366                                if( !optAttr.isEmpty() ) {
367                                        editAttri.add( "optionAttributes" , optAttr );
368        //                              config.setEditorParam( optAttr );
369                                }
370
371                                // dbType が、TP_MENU かTP_RADIO の場合は、DBMENUの引数($2)に、トークンを渡す。(GG03を検索する)
372                                String tempVal = val;
373                                if( "TP_MENU".equals( dbType ) || "TP_RADIO".equals( dbType ) ) {
374                                        config.setAddNoValue( true );           // プルダウンメニューは、空欄を選択できるようにしておく
375                                        tempVal = val + ":" + token;            // DBMENUの引数($2)
376                                }
377
378                                valBuf = new DBColumn( config ).getEditorValue( row,tempVal );          // 入力欄(Editor)
379                        }
380
381                        // 理由 があれば、valBuf の直後にそのまま入れる。
382                        final String riyu= rowData( row,noRiyu,"" );
383                        if( ! riyu.isEmpty() ) {
384                                valBuf = valBuf + riyu ;
385                        }
386
387                        final int colspan = rowData( row,noColspan,1 );
388                        final int rowspan = rowData( row,noRowspan,1 );
389
390                        // 表示方法(CDDISP) 1:ラベルカラム 2:カラム単発 3:カラム連続 4:表示のみ で、
391                        // 3:カラム連続 の場合のみ、</td> の直前に INSERT する。
392
393                        // ラベル項目の表示
394                        if( "1".equals( cddisp ) ) {                                                    // CDDISP=1:ラベルカラム の場合、ラベル表示される(colspanなし)
395                                if( 1 == rowspan ) {
396                                        tagOut.append( "<td class='label'>" );
397                                }
398                                else {
399                                        tagOut.append( "<td class='label' rowspan=\"" ).append( rowspan ).append( "\">" );
400                                }
401                                tagOut.append(lbl).append( "</td>" );
402                        }
403
404                        if( "1".equals( cddisp ) || "2".equals( cddisp ) ) {    // CDDISP=1:ラベルカラム or 2:カラム単発 の場合 td が必要。
405                                tagOut.append( "<td" );
406                                // colspan を反映させるのは、フィールド部のみ
407                                if( 1 != colspan ) {
408                                        tagOut.append( " colspan=\"" ).append( colspan ).append( '"' ); // ラベルと本体があるが自分で調整する。
409                                }
410
411                                // rowspan を反映させるのは、フィールド部のみ
412                                if( 1 != rowspan ) {
413                                        tagOut.append( " rowspan=\"" ).append( rowspan ).append( '"' );
414                                }
415                                tagOut.append( '>' ).append( valBuf ).append( INS_VAL );                        // INS_VAL は、CDDISP=3:カラム連続 の場合の挿入位置
416                                taniErrWarn( row,tagOut );
417                        }
418
419                        // 4:表示のみ の場合は、td を出す。
420                        if( "4".equals( cddisp ) ) {                                                    // CDDISP=4:表示のみ の場合 td が必要。
421                                tagOut.append( "<td" );
422                                // colspan を反映させる
423                                if( 1 != colspan ) {
424                                        tagOut.append( " colspan=\"" ).append( colspan ).append( '"' ); // colspan をそのまま出す。自分で調整する。
425                                }
426
427                                // rowspan を反映させるのは、フィールド部のみ
428                                if( 1 != rowspan ) {
429                                        tagOut.append( " rowspan=\"" ).append( rowspan ).append( '"' );
430                                }
431                                tagOut.append( '>' ).append( valBuf ).append( "</td>" );        // valBuf を使うが、先にRenderer の値を設定している。
432                        }
433
434                        if( "3".equals( cddisp ) ) {                                                    // CDDISP=3:カラム連続 の場合は、INS_VALの直前に挿入する。
435                                final int lstIdx = tagOut.lastIndexOf( INS_VAL ) ;
436                                if( lstIdx >= 0 ) {
437                                        tagOut.insert( lstIdx,valBuf );
438                                }
439                                else {                                                  // ありえないはずだが、挿入位置が見つからない場合は、td で囲う。
440                                        tagOut.append( "<td>" ).append( valBuf ).append( INS_VAL ).append( "</td>" );
441                                }
442                        }
443                }
444
445                tagOut.append(" </tr></tbody></table>").append( CR );
446                if( !bkSelNm.isEmpty() ) { tagOut.append( "</fieldset>" ); }
447                tagOut.append( getScrollBarEndDiv() );
448
449                return tagOut.toString().replace( INS_VAL , "" );                       // 最後に文字列に変換後、INS_VAL は、すべて空文字列に置き換えます。
450        }
451
452        /**
453         * 値出力の終端処理をまとめて行います。
454         *
455         * 全体出力の tagOut に、値フィールドの valBuf と、 tani を連結し、
456         * DBTableModelから、異常下限,警告下限,警告上限,異常上限を、div で固めます。
457         * valBuf は、初期化されます。
458         *
459         * @og.rev 7.3.1.1 (2021/02/25) USE_CSV 属性追加
460         *
461         * @param       row 行
462         * @param       tagOut 全体出力のStringBuilder
463         * @param       valBuf 値フィールドのStringBuilder(このメソッド内でクリアする)
464         * @param       tani 単位
465         */
466//      private void valBufOut( final int row,final StringBuilder tagOut,final StringBuilder valBuf,final String tani ) {
467        private void taniErrWarn( final int row,final StringBuilder tagOut ) {
468                final String tani = rowData( row,noTani,"(",")" );      // 単位表示(カラムの最後に出す)
469                tagOut.append( tani );
470
471                final String wmin = rowData( row,noWmin,"" );           // 警告下限(W_MIN)
472                final String wmax = rowData( row,noWmax,"" );           // 警告上限(W_MAX)
473                final String emin = rowData( row,noEmin,"" );           // 異常下限(E_MIN)
474                final String emax = rowData( row,noEmax,"" );           // 異常上限(E_MAX)
475
476                if( wmin.isEmpty() && wmax.isEmpty() && emin.isEmpty() && emax.isEmpty() ) {
477                        tagOut.append( "</td>" );
478                }
479                else {
480                        tagOut.append( "<div class='small' title='上段:警告&#10;下段:異常'>" );
481
482                        if( !wmin.isEmpty() || !wmax.isEmpty() ) {                      // 警告下限(W_MIN) 警告上限(W_MAX)
483                                final String max = "%" + Math.max( wmin.length() , wmax.length() ) + "s";       // 最大桁数を求める。
484                                tagOut.append( String.format( max + "~" + max ,wmin,wmax ) );
485                        }
486                        tagOut.append( "</br>" );
487
488                        if( !emin.isEmpty() || !emax.isEmpty() ) {                      // 異常下限(E_MIN) 異常上限(E_MAX)
489                                final String max = "%" + Math.max( emin.length() , emax.length() ) + "s";       // 最大桁数を求める。
490                                tagOut.append( String.format( max + " ~ " + max ,emin,emax ) );                 // 異常の範囲の方が広いので、間を少し開けている。
491                        }
492                        tagOut.append( "</div></td>" );
493                }
494        }
495
496        /**
497         * DBTableModelから、指定行-列のデータを取得します。
498         *
499         * 列番号がマイナスの場合は、カラムが存在していないため、初期値を返します。
500         *
501         * @og.rev 7.3.1.1 (2021/02/25) USE_CSV 属性追加
502         *
503         * @param       row 行
504         * @param       col 列
505         * @param       defVal 初期値(文字列)
506         *
507         * @return 指定行-列のデータ(文字列)
508         */
509        private String rowData( final int row , final int col , final String defVal ) {
510                String rtn = defVal ;
511                if( col >= 0 ) {
512                        final String str = table.getValue( row,col );
513                        if( str != null && !str.isEmpty()) {
514                                rtn = str ;
515                        }
516                }
517                return rtn ;
518        }
519
520        /**
521         * DBTableModelから、指定行-列のデータを取得し、前後に文字列を追加します。
522         *
523         * 列番号がマイナスの場合は、カラムが存在していないため、長さゼロの文字列を返します。
524         * 取得したデータが、長さゼロの文字列でない場合は、前後に指定の文字列を追加して返します。
525         *
526         * @og.rev 7.3.1.1 (2021/02/25) USE_CSV 属性追加
527         *
528         * @param       row 行
529         * @param       col 列
530         * @param       prefix 前に付ける文字列
531         * @param       suffix 後ろに付ける文字列
532         *
533         * @return 指定行-列のデータ(文字列)
534         */
535        private String rowData( final int row , final int col , final String prefix , final String suffix ) {
536                String rtn = "" ;
537                if( col >= 0 ) {
538                        final String str = table.getValue( row,col );
539                        if( str != null && !str.isEmpty()) {
540                                rtn = prefix + str + suffix ;
541                        }
542                }
543                return rtn ;
544        }
545
546        /**
547         * DBTableModelから、指定行-列のデータを取得します。
548         *
549         * 列番号がマイナスの場合は、カラムが存在していないため、初期値を返します。
550         *
551         * @og.rev 7.3.1.1 (2021/02/25) USE_CSV 属性追加
552         *
553         * @param       row 行
554         * @param       col 列
555         * @param       defVal 初期値(数値)
556         *
557         * @return 指定行-列のデータ(数値)
558         */
559        private int rowData( final int row , final int col , final int defVal ) {
560                final String val = col < 0 ? null : table.getValue( row,col ) ;
561
562                try {
563                        return val == null || val.isEmpty() ? defVal : Integer.parseInt( val );
564                }
565                catch( final NumberFormatException ex ) {
566                        final String errMsg = "数値項目に数値以外の文字が含まれています。行=" + row + " , 列=" + col + " , 値=" + val ;
567                        throw new HybsSystemException( errMsg,ex );
568                }
569        }
570
571        /**
572         * DBタイプ TP_YMD、TP_HMS、TP_YMDH の初期値に現在時刻を求めます。
573         *
574         * DBタイプが、上記以外か、val が NOW 以外の場合は、val をそのまま返します。
575         *
576         * @og.rev 7.3.1.1 (2021/02/25) USE_CSV 属性追加
577         *
578         * @param       dbType  DBタイプ TP_YMD、TP_HMS、TP_YMDH 以外は、引数のval をそのまま返します。
579         * @param       val             変換する文字列 NOW 以外の場合は、引数のval をそのまま返します。
580         *
581         * @return 日時タイプの初期値を求めます
582         */
583        private String timeVal( final String dbType , final String val ) {
584                String rtn = val;
585                if( "NOW".equalsIgnoreCase( val ) ) {
586                        final LocalDateTime now = LocalDateTime.now();
587
588                        if( "TP_YMD".equals(dbType) ) {
589                                rtn = now.format( YMD );
590                        }
591                        else if( "TP_HMS".equals(dbType) ) {
592                                rtn = now.format( HM );
593                        }
594                        else if( "TP_YMDH".equals(dbType) ) {
595                                rtn = now.format( YMDHMS );
596                        }
597                }
598
599                return rtn ;
600        }
601
602        /**
603         * オプション属性の特殊記号を変換します。
604         *
605         * シングルクオートは必要です。『=』から後ろ全てをそのまま 適用します。
606         *
607         * 【これらの設定は、Attributesオブジェクトに設定します】
608         *  P='AAA'                    → placeholder='AAA'
609         *  T='ツールチップ'                → title='ツールチップ'
610         *  C='HALF'                   → class='HALF'
611         *  S='XX:BB;YY:CC;'           → style='XX:BB;YY:CC;'
612         *
613         * 【これらは変換して属性に設定します】
614         *  W='120px'                  → width='120px'
615         *  H='300px'                  → height='300px'
616         *  D='GRP1'                   → dis='GRP1'                             DIS で指定したグループを disabled するときのキー
617         *
618         * 【DISはonchangeで書き込み不可制御を行います】
619         *  DIS(・・・・)                  → onchange=disabl(this.・・・・)
620         *    以下、変換は同一です。
621         *  DIS(value=='123','GRP1')   → onchange=disabl(this.value=='123','GRP1')              textfieldなど
622         *  DIS(checked,'GRP1')        → onchange=disabl(this.checked,'GRP1')                   チェックボックス
623         *  DIS(selectedIndex==2,'GRP1')                       → onchange=disabl(selectedIndex==2,'GRP1')       プウルダウンメニュー(インデックス判定)
624         *  DIS(options[selectedIndex].value=='テスト2','GRP1')   → onchange=disabl(options[selectedIndex].value=='テスト2','GRP1')   プウルダウンメニュー(値判定)
625         *
626         * 【ANVは、addNoValue制御の略で、メニューに空オプションを追加できます】
627         *  ANV='true'                                  → DBColumnConfig#setAddNoValue(true) を設定します。
628         * 【AKLは、addKeyLabel制御の略で、trueでキー:ラベル形式、falseでラベルのみ、null(未指定)はリソースに準拠します】
629         *  AKL='true'                                  → DBColumnConfig#setAddKeyLabel("true") を設定します。
630         *
631         * 【それ以外は、変換なしで設定します】
632         *  readonly                   → readonly               (例)
633         *
634         * @og.rev 7.3.1.1 (2021/02/25) オプション属性の特殊記号を変換
635         * @og.rev 7.4.2.3 (2021/06/09) addNoValue制御、addKeyLabel制御を追加
636         *
637         * @param       attri   Attributesオブジェクト
638         * @param       val             変換するオプション属性
639         *
640         * @return 変換後のオプション属性
641         */
642//      private String changeOptAttr( final Attributes attri,final String val ) {
643        private String changeOptAttr( final DBColumnConfig config , final Attributes attri,final String val ) {
644                if( val.isEmpty() ) { return val; }                             // 確率的に、空文字はそのまま返します。
645
646                final StringBuilder buf = new StringBuilder( val );     // 最終的に出力するタグの文字列バッファ
647
648                int st = 0;
649                int ed = 0;
650
651                st = buf.indexOf( "P=" );
652                if( st >= 0 ) {
653                        ed = Math.max( buf.indexOf( "'" , st+3 ),buf.indexOf( "\"" , st+3 ) );          // シングルか、ダブルか
654                        attri.set( "placeholder" , buf.substring( st+3,ed ) );  // P= の後ろ+1から、最後のコーテーション(含まず)まで
655                        buf.delete( st,ed+1 );
656                }
657
658                st = buf.indexOf( "T=" );
659                if( st >= 0 ) {
660                        ed = Math.max( buf.indexOf( "'" , st+3 ),buf.indexOf( "\"" , st+3 ) );
661                        attri.set( "title" , buf.substring( st+3,ed ) );                // T= の後ろ+1から、最後のコーテーション(含まず)まで
662                        buf.delete( st,ed+1 );
663                }
664
665                st = buf.indexOf( "C=" );
666                if( st >= 0 ) {
667                        ed = Math.max( buf.indexOf( "'" , st+3 ),buf.indexOf( "\"" , st+3 ) );
668                        attri.add( "class" , buf.substring( st+3,ed ) );                // T= の後ろ+1から、最後のコーテーション(含まず)まで
669                        buf.delete( st,ed+1 );
670                }
671
672                st = buf.indexOf( "S=" );
673                if( st >= 0 ) {
674                        ed = Math.max( buf.indexOf( "'" , st+3 ),buf.indexOf( "\"" , st+3 ) );
675                        attri.add( "style" , buf.substring( st+3,ed ) );                // T= の後ろ+1から、最後のコーテーション(含まず)まで
676                        buf.delete( st,ed+1 );
677                }
678
679                // 7.4.2.3 (2021/06/09) addNoValue制御、addKeyLabel制御を追加
680                st = buf.indexOf( "ANV=" );
681                if( st >= 0 ) {
682                        ed = Math.max( buf.indexOf( "'" , st+5 ),buf.indexOf( "\"" , st+5 ) );
683                        config.setAddNoValue( Boolean.parseBoolean( buf.substring( st+5,ed ) ) );
684                        buf.delete( st,ed+1 );
685                }
686
687                // 7.4.2.3 (2021/06/09) addNoValue制御、addKeyLabel制御を追加
688                st = buf.indexOf( "AKL=" );
689                if( st >= 0 ) {
690                        ed = Math.max( buf.indexOf( "'" , st+5 ),buf.indexOf( "\"" , st+5 ) );
691                        config.setAddKeyLabel( buf.substring( st+5,ed ) );              // 空文字列は、処理不定
692                        buf.delete( st,ed+1 );
693                }
694
695                return buf.toString()
696                                .replace( "W=" , "width=" )
697                                .replace( "H=" , "height=" )
698                                .replace( "D=" , "dis=" )
699                                .replace( "DIS(" , "onchange=disabl(this." ) ;
700        }
701
702        /**
703         * DBTableModel から テーブルのヘッダータグ文字列を作成して返します。
704         *
705         * @og.rev 3.5.2.0 (2003/10/20) ヘッダーそのもののキャッシュはしない。
706         *
707         * @return      テーブルのヘッダータグ文字列
708         * @og.rtnNotNull
709         */
710//      protected String getHeader() {
711//              // ヘッダーは不要なので、オーバーライドしておく。
712//              return "" ;
713//      }
714}