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