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