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