    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.table;
    017
    018import org.opengion.hayabusa.db.AbstractTableFilter;
    019import org.opengion.hayabusa.db.DBTableModel;
    020import org.opengion.hayabusa.db.DBColumn;
    021
    022import org.opengion.hayabusa.resource.ResourceFactory;
    023import org.opengion.hayabusa.resource.ResourceManager;
    024import org.opengion.hayabusa.resource.CodeData;
    025
    026import org.opengion.fukurou.util.ErrorMessage;
    027import org.opengion.fukurou.util.StringUtil;
    028
    029import java.util.Locale;
    030
    031/**
    032 * TableFilter_CLMSET は、TableFilter インターフェースを継承した、DBTableModel 処理用の
    033 * 実装クラスです。
    034 *
    035 * ここでは、CLM,SYSTEM_ID,LANG より、カラムリソースのRENDERER,EDITOR,DBTYPE,BIKOを設定します。
    036 * 検索した DBTableModel の属性として、RENDERER,EDITOR,DBTYPE,BIKO という名称の
    037 * カラムが必要です。
    038 * 引数として指定可能なのは、SYSTEM_ID,LANG のみです。
    039 *  CLM :カラムリソースのキーとなる値が設定されているカラム名を指定します。
    040 *  SYSTEM_ID:コードリソースの作成システムIDを指定します。無指定時は、ログイン時のリソースになります。
    041 *  LANG:ラベルリソースの言語を指定します。無指定時は、日本語になります。
    042 *  USE_RESOURCE:リソース情報を利用するかどうか[true/false]。無指定時は、true:利用するになります。
    043 *
    044 * また、CLM,RENDERER,EDITOR,DBTYPE,BIKO,CLS_NAME,USE_LENGTH で指定したカラムが DBTableModel に存在しない場合は、
    045 * 処理そのものを無視します。その場合は、警告も出力されませんので、ご注意ください。
    046 *
    047 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。
    048 * 【パラメータ】
    049 *  {
    050 *       USE_RESOURCE   : [true/false] ;    リソースを利用するかどうかを指定[true/false](初期値:true)。使う場合、DBColumnを構築し、そこから RENDERE 等を取得します。
    051 *       CLM            : CLM ;             カラムリソースのキーとなる値が設定されているカラム名を指定します。
    052 *       SYSTEM_ID      : GF ;              リソースを使う場合に、コードリソースの作成システムIDを指定します。
    053 *       LANG           : ja ;              リソースを使う場合に、ラベルリソースの言語を指定します。
    054 *       RENDERER       : 設定するカラム名
    055 *       EDITOR         : 設定するカラム名
    056 *       DBTYPE         : 設定するカラム名
    057 *       BIKO           : 設定するカラム名
    058 *       CLS_NAME       : 設定するカラム名
    059 *       USE_LENGTH     : 設定するカラム名
    060 *  }
    061 *
    062 * @og.formSample
    063 * ●形式：
    064 *      ① &lt;og:tableFilter classId="CLMSET" keys="USE_RESOURCE,CLM,SYSTEM_ID,RENDERER,EDITOR,DBTYPE,BIKO,CLS_NAME,USE_LENGTH"
    065 *                                             vals="true,CLM,SYSTEM_ID,RENDERER,EDITOR,DBTYPE,BIKO,CLS_NAME,USE_LENGTH" /&gt;
    066 *
    067 *      ② &lt;og:tableFilter classId="CLMSET" &gt;
    068 *               {
    069 *                       USE_RESOURCE  : true         ;
    070 *                       CLM           : CLM          ;
    071 *                       SYSTEM_ID     : SYSTEM_ID    ;
    072 *                       RENDERER      : RENDERER     ;
    073 *                       EDITOR        : EDITOR       ;
    074 *                       DBTYPE        : DBTYPE       ;
    075 *                       BIKO          : BIKO         ;
    076 *                       CLS_NAME      : CLS_NAME     ;
    077 *                       USE_LENGTH    : USE_LENGTH   ;
    078 *               }
    079 *         &lt;/og:tableFilter&gt;
    080 *
    081 * @og.rev 4.1.0.0(2008/01/18) 新規作成
    082 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加
    083 *
    084 * @version  0.9.0  2000/10/17
    085 * @author   Kazuhiko Hasegawa
    086 * @since    JDK1.5,
    087 */
    088public class TableFilter_CLMSET extends AbstractTableFilter {
    089        /** このプログラムのVERSION文字列を設定します。   {@value} */
    090        private static final String VERSION = "6.5.0.1 (2016/10/21)" ;
    091
    092        private static final int NO_CLM  = 0;
    093        private static final int NO_CLS  = 1;
    094        private static final int NO_LEN1 = 2;
    095        private static final int NO_LEN2 = 3;
    096        private static final int NO_BIKO = 4;
    097        private static final int NO_REN  = 5;
    098        private static final int NO_EDI  = 6;
    099        private static final int NO_TYP  = 7;
    100
    101        // 5.5.8.2 (2012/11/09) RENDERER,EDITOR,DBTYPE の条件分けを変更します。
    102        // 処理は、上から順番に判定していきますので、ご注意ください。
    103        /** RENDERER,EDITOR,DBTYPE の条件分けの マスターデータレコード を設定します。   {@value} */
    104        private static final String[][] MASTER_DATA = {
    105        //   clm        , cls   , =len  , >=len , biko  , renderer      , editor        , dbtype
    106                { "DY"  , null  , "4"   , null  , null  , "MD"          , "TEXT"        , "X"           } ,             // 日付文字列(時分)
    107                { "DY"  , null  , "6"   , null  , null  , "YM"          , "YM"          , "YM"          } ,             // 日付文字列(年日)
    108                { "DY"  , null  , "8"   , null  , null  , "YMD"         , "YMD"         , "YMD"         } ,             // 日付文字列(年月日)
    109                { "DY"  , null  , "14"  , null  , null  , "YMDH"        , "YMDH"        , "YMDH"        } ,             // 日付文字列(年月日時分秒)
    110                { "TM"  , null  , "4"   , null  , null  , "HM"          , "TEXT"        , "HM"          } ,             // 時間文字列(時分)
    111                { "TM"  , null  , "6"   , null  , null  , "HMS"         , "TEXT"        , "HMS"         } ,             // 時間文字列(時分秒)
    112                { "CD"  , null  , null  , null  , ":"   , "MENU"        , "MENU"        , "X"           } ,             // CODEリソース
    113                { "FG"  , null  , null  , null  , ":"   , "MENU"        , "MENU"        , "X"           } ,             // CODEリソース
    114                { "KB"  , null  , null  , null  , ":"   , "MENU"        , "MENU"        , "X"           } ,             // CODEリソース
    115                { null  , "NU"  , "1"   , null  , ":"   , "MENU"        , "MENU"        , "S9"          } ,             // NUMBER(整数)
    116                { null  , "IN"  , "1"   , null  , ":"   , "MENU"        , "MENU"        , "S9"          } ,             // INTEGER , INT64
    117                { null  , null  , "1"   , null  , ":"   , "MENU"        , "MENU"        , "X"           } ,             // CODEリソース
    118                { null  , "VA"  , null  , "30"  , null  , "LABEL"       , "TEXT"        , "KX"          } ,             // VARCHAR , VARCHAR2(30桁以上は漢字)
    119                { null  , "VA"  , null  , null  , null  , "LABEL"       , "TEXT"        , "X"           } ,             // VARCHAR , VARCHAR2(30桁以下は英数字)
    120                { null  , "NU"  , ","   , null  , null  , "NUMBER"      , "NUMBER"      , "R"           } ,             // NUMBER(小数)
    121                { null  , "NU"  , null  , null  , null  , "NUMBER"      , "NUMBER"      , "S9"          } ,             // NUMBER(整数)
    122                { null  , "IN"  , null  , null  , null  , "NUMBER"      , "NUMBER"      , "S9"          } ,             // INTEGER , INT64
    123                { null  , "DE"  , null  , null  , null  , "NUMBER"      , "NUMBER"      , "R"           } ,             // DECIMAL
    124                { null  , "TI"  , "8"   , null  , null  , "YMD"         , "YMD"         , "YMD"         } ,             // TIMESTAMP(8桁)
    125                { null  , "TI"  , "14"  , null  , null  , "YMDH"        , "YMDH"        , "YMDH"        } ,             // TIMESTAMP(14桁)
    126                { null  , "TI"  , null  , null  , null  , "DATE"        , "YMDH"        , "DATE"        } ,             // TIMESTAMP
    127                { null  , "DA"  , "8"   , null  , null  , "DATE"        , "YMD"         , "DATE"        } ,             // DATE(8桁)
    128                { null  , "DA"  , null  , null  , null  , "DATE"        , "YMDH"        , "DATE"        } ,             // DATE
    129                { null  , "CH"  , null  , null  , null  , "LABEL"       , "TEXT"        , "X"           } ,             // CHAR
    130                { null  , "CL"  , null  , null  , null  , "LABEL"       , "TEXT"        , "KX"          } ,             // CLOB
    131                { null  , null  , null  , null  , null  , "LABEL"       , "TEXT"        , "X"           }               // その他
    132        } ;
    133
    134        /**
    135         * デフォルトコンストラクター
    136         *
    137         * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。
    138         */
    139        public TableFilter_CLMSET() {
    140                super();
    141                initSet( "USE_RESOURCE" , "リソースを利用するかどうかを指定[true/false](初期値:true)"      );
    142                initSet( "CLM"                  , "カラムリソースのキーとなる値が設定されているカラム名を指定"       );
    143                initSet( "SYSTEM_ID"    , "リソースを使う場合に、コードリソースの作成システムIDを指定"      );
    144                initSet( "LANG"                 , "リソースを使う場合に、ラベルリソースの言語を指定"                    );
    145                initSet( "RENDERER"     , "設定するカラム名"                                                                                    );
    146                initSet( "EDITOR"               , "設定するカラム名"                                                                                    );
    147                initSet( "DBTYPE"               , "設定するカラム名"                                                                                    );
    148                initSet( "BIKO"                 , "設定するカラム名"                                                                                    );
    149                initSet( "CLS_NAME"     , "設定するカラム名"                                                                                    );
    150                initSet( "USE_LENGTH"   , "設定するカラム名"                                                                                    );
    151        }
    152
    153        /**
    154         * DBTableModel処理を実行します。
    155         *
    156         * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更
    157         * @og.rev 5.5.7.4 (2012/10/25) 備考欄の処理は、ここでは行いません。
    158         * @og.rev 5.5.8.2 (2012/11/09) RENDERER,EDITOR,DBTYPE の条件分けを変更します。
    159         * @og.rev 5.5.8.5 (2012/11/27) USE_RESOURCE 引数追加
    160         * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
    161         *
    162         * @return 処理結果のDBTableModel
    163         */
    164        public DBTableModel execute() {
    165                final DBTableModel table = getDBTableModel();           // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加
    166
    167                final boolean useResource = StringUtil.nval( getValue("USE_RESOURCE"), true );          // 5.5.8.5 (2012/11/27) USE_RESOURCE 引数追加
    168
    169                // 5.5.8.5 (2012/11/27) 初期化タイミングを遅らします。
    170                // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    171                final ResourceManager resource ;
    172
    173                if( useResource ) {
    174                        final String systemId   = getValue( "SYSTEM_ID" );
    175                        final String lang               = getValue( "LANG" );
    176                        resource = ResourceFactory.newInstance( systemId,lang,false );
    177                }
    178                else {
    179                        resource = null;                        // 6.3.9.1 (2015/11/27)
    180                }
    181
    182                final int clmNo  = table.getColumnNo( "CLM",false );            // 存在しない場合は、-1 を返す。
    183                final int renNo  = table.getColumnNo( "RENDERER",false );
    184                final int ediNo  = table.getColumnNo( "EDITOR",false );
    185                final int typNo  = table.getColumnNo( "DBTYPE",false );
    186                final int bikoNo = table.getColumnNo( "BIKO",false );
    187
    188                final int clsNo  = table.getColumnNo( "CLS_NAME",false );                       // 5.5.7.4 (2012/10/25) CLS_NAME カラム番号の取得
    189                final int lenNo  = table.getColumnNo( "USE_LENGTH",false );             // 5.5.7.4 (2012/10/25) USE_LENGTH カラム番号の取得
    190
    191                if( clmNo >= 0 && renNo >= 0 && ediNo >= 0 && typNo >= 0 ) {
    192                        String[] data  = null;
    193                        final int rowCnt = table.getRowCount();
    194                        // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD)
    195                        for( int row=0; row<rowCnt; row++ ) {
    196                                String clmVal = null;
    197                                try {
    198                                        data   = table.getValues( row );
    199                                        clmVal = data[clmNo].trim().toUpperCase(Locale.JAPAN);          // 変換する元のカラム名
    200                                        // 5.5.8.5 (2012/11/27) USE_RESOURCE 引数追加。
    201                                        // 6.3.9.1 (2015/11/27) useResource == true でないと、column は null のままなので、if条件を useResource で判定する。
    202                                        if( useResource ) {
    203                                                final DBColumn column = resource.getDBColumn( clmVal );
    204                                                data[renNo] = column.getRenderer() ;
    205                                                data[ediNo] = column.getEditor() ;
    206                                                data[typNo] = column.getDbType() ;
    207
    208                                                // 5.5.7.4 (2012/10/25) BIKO の設定は、検索時にカラムがあり、MENUレンデラーで、備考がNULLの場合のみ再設定する。
    209                                                if( bikoNo >= 0 && "MENU".equalsIgnoreCase( data[renNo] ) && ( data[bikoNo] == null || data[bikoNo].isEmpty() ) ) {
    210                                                        final CodeData code = resource.getCodeData( clmVal );
    211                                                        if( code != null ) {
    212                                                                data[bikoNo] = code.toCodeString() ;
    213                                                        }
    214                                                }
    215                                        }
    216                                        // 5.5.8.2 (2012/11/09) RENDERER,EDITOR,DBTYPE の条件分けを変更します。
    217                                        else {
    218                                                final String clsVal  = clsNo  < 0 || data[clsNo]  == null ? "" : data[clsNo].trim().toUpperCase(Locale.JAPAN);
    219                                                String lenVal        = lenNo  < 0 || data[lenNo]  == null ? "" : data[lenNo].trim().replace( '.',',' ) ;
    220                                                final String bikoVal = bikoNo < 0 || data[bikoNo] == null ? "" : data[bikoNo];
    221
    222                                                // 長さで、小数部が、 0 の場合は、整数のみと判断する。
    223                                                final int cm = lenVal.indexOf( ",0" );
    224                                                if( cm >= 0 ) { lenVal = lenVal.substring( 0,cm ); }
    225
    226                                                final String[] selData = serchMasterData( clmVal,clsVal,lenVal,bikoVal );
    227
    228                                                // 副作用を及ぼします。
    229                                                data[renNo]  = selData[NO_REN];
    230                                                data[ediNo]  = selData[NO_EDI];
    231                                                data[typNo]  = selData[NO_TYP];
    232
    233                                                if( lenVal.contains( "," ) ) { data[lenNo] = lenVal ; }         // "," が含まれている場合は、再設定
    234                                        }
    235                                }
    236                                catch( final RuntimeException ex ) {
    237                                // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。
    238                                        makeErrorMessage( "TableFilter_CLMSET Error",ErrorMessage.NG )
    239                                                .addMessage( row+1,ErrorMessage.NG,"CLMSET"
    240                                                        , "CLM=[" + clmVal + "]"
    241                                                        , StringUtil.array2csv( data )
    242                                                )
    243                                                .addMessage( ex );
    244                                }
    245                        }
    246                }
    247
    248                return table;
    249        }
    250
    251        /**
    252         * RENDERER,EDITOR,DBTYPE の条件分けを、マスターデータレコードから見つけて、対応するレコードの配列を返します。
    253         *
    254         * これは、マスタデータは、順番に評価していき、最初に成立した値をセットします。
    255         * マスタデータは、clm,cls,len,biko について条件設定しておき、条件が成立するかどうか判断し、成立するレコードを
    256         * 返すことで、そのレコードに該当する、RENDERER,EDITOR,DBTYPE の値を返します。
    257         * これで、ある程度複雑な条件判定が可能になります。
    258         * マスタデータの null は、条件に含めない（無条件成立）を意味します。
    259         *
    260         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
    261         *
    262         * @param  clmVal       カラムデータ(not null保障 , 大文字保障)
    263         * @param  clsVal       クラスデータ(not null保障 , 大文字保障)
    264         * @param  lenVal       長さデータ(not null , カンマ保障)
    265         * @param  bikoVal      備考データ(not null保障)
    266         * @return レコードの配列
    267         */
    268        private String[] serchMasterData( final String clmVal,final String clsVal,final String lenVal,final String bikoVal ) {
    269                final String lenVal2  = lenVal.contains( "," ) ? "," : lenVal ; // 小数を含む場合は、"," を、そうでない場合は、そのまま返す。
    270
    271                final int size = MASTER_DATA.length;
    272                for( int i=0; i<size; i++ ) {
    273                        final String[] rowData = MASTER_DATA[i];
    274                        if( ( rowData[NO_CLM]  == null || clmVal.startsWith( rowData[NO_CLM] ) ) &&
    275                                ( rowData[NO_CLS]  == null || clsVal.startsWith( rowData[NO_CLS] ) ) &&
    276                                ( rowData[NO_LEN1] == null || rowData[NO_LEN1].equalsIgnoreCase( lenVal2 ) ) &&
    277                                ( rowData[NO_LEN2] == null || Integer.parseInt( lenVal ) >= Integer.parseInt( rowData[NO_LEN2] ) ) &&
    278                                ( rowData[NO_BIKO] == null || bikoVal.contains( rowData[NO_BIKO] ) ) ) {
    279                                return rowData ;
    280                        }
    281                }
    282
    283                return MASTER_DATA[size-1] ;    // MASTER_DATA の最終レコードでマッチするので、来ないはず
    284        }
    285}