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.hayabusa.taglib;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.hayabusa.common.HybsSystemException;
020import org.opengion.hayabusa.db.DBTableModel;
021import org.opengion.fukurou.db.DBUtil;
022
023import org.opengion.fukurou.util.ErrorMessage;
024import org.opengion.fukurou.util.StringUtil ;
025import static org.opengion.fukurou.util.StringUtil.nval ;
026import org.opengion.fukurou.model.Formatter;
027
028import java.util.Locale ;
029import java.util.List ;
030import java.util.ArrayList ;
031
032import java.io.ObjectOutputStream;
033import java.io.ObjectInputStream;
034import java.io.IOException;
035
036/**
037 * 【廃止】登録すべきデータのマスタ存在チェックを行うためのタグです(通常はentry.jspでupdateタグの直前で使用します)。
038 *
039 * この要素の内容に、SQL文を記述します。
040 * names に対応するカラム名を、カンマ区切りで複数与えます。その値を、DBTableModel
041 * より、取得し、先のSQL文の ? に値を設定します。
042 * または、引数部に、[カラム名]を用いたHybs拡張SQL文を指定することも可能です。
043 *
044 * 値の取得は、先に選択された行のみについて、実行されます。
045 * exist 属性に指定された 、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、
046 * の値は、チェック方法を設定しています。
047 * いずれの場合も、成立時は、正常とみなします。
048 * (「true:存在する」 には、データが存在した場合に、OKで、なければエラーです。)
049 *
050 * @og.formSample
051 * ●形式:
052 *       ・<og:tableExist
053 *                    command   = "{@command}"
054 *                    names     = "[…]"
055 *                    from      = "…"                           必須
056 *                    where     = "…"                           必須
057 *                    exist     = "[auto|true|false|one|notuse]" 必須
058 *                    errRemove = "[true|false]"
059 *         />
060 *
061 * ●body:なし
062 *
063 * ●Tag定義:
064 *   <og:tableExist
065 *       command            【廃止】コマンド(ENTRY)をセットします
066 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/applicaton]を指定します(初期値:session)
067 *       names              【廃止】引数にセットすべき データの名称(カラム名)をCSV形式で複数指定します
068 *       from             ○【廃止】チェックするデータベース名(from 句)を指定します(必須)。
069 *       where            ○【廃止】チェックする検索条件(where句)を指定します(必須)。
070 *       exist              【廃止】データベースのチェック方法(auto/true/false/one/notuse)を指定します(初期値:「auto:自動」)
071 *       tableId            【廃止】(通常は使いません)結果をDBTableModelに書き込んで、sessionに登録するときのキーを指定します
072 *       dbid               【廃止】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します
073 *       errRemove          【廃止】エラー時の選択行を取り除いて継続処理を行うかどうか[true/false]を指定します(初期値:false)
074 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
075 *   />
076 *
077 * ●使用例
078 *       ・<og:tableExist
079 *                    command = "{@command}"
080 *                    names   = "USERID,SYSTEM_ID"
081 *                    from    = "GE10"
082 *                    where   = "USERID=? AND SYSTEM_ID=?"
083 *                    exist   = "true"         />
084 *
085 *          ・where 条件の ? 文字に、names で指定したカラム名の値が、DBTableModelより
086 *            取得されます。
087 *            値の取得は、先に選択された行のみについて、実行されます。
088 *          ・exist 属性の値に応じて、チェック方法が異なります。
089 *            auto , true , false , one , notuse が指定できます。
090 *          ・テーブルは、1つのみ指定できます。複数指定や、UNIONで結合する場合は、
091 *            ビュー等を作成して対応してください。
092 *
093 *       ・<og:tableExist
094 *                    command = "{@command}"
095 *                    from    = "GE10"
096 *                    where   = "USERID=[USERID] AND SYSTEM_ID=[SYSTEM_ID]"  />
097 *
098 *          ・where 条件の [カラム名] 文字に、DBTableModelより値がセットされます。
099 *          ・exist は、初期値(auto)になります。内部のA,C,Dに応じて自動判別します。
100 *
101 * @og.rev 3.5.0.0 (2003/09/17) 新規作成
102 * @og.group (廃止)DB登録
103 *
104 * @version  4.0
105 * @author       Kazuhiko Hasegawa
106 * @since    JDK5.0,
107 * @deprecated 代わりに dataCheckタグを使用してください
108 */
109@Deprecated public class TableExistTag extends CommonTagSupport {
110        //* このプログラムのVERSION文字列を設定します。   {@value} */
111        private static final String VERSION = "4.0.0.0 (2007/11/28)" ;
112
113        private static final long serialVersionUID = 400020071128L ;
114
115        /** command 引数に渡す事の出来る コマンド  エントリー {@value} */
116        public static final String CMD_ENTRY   = "ENTRY" ;
117
118        /** command 引数に渡す事の出来る コマンド リスト  */
119        private static final String COMMAND_LIST = CMD_ENTRY;
120
121        // 3.5.6.0 (2004/06/18) すべてを protected から private に変更します。
122        private transient DBTableModel  table           = null;
123        private transient ErrorMessage  errMessage      = null;
124        private String                  tableId         = HybsSystem.TBL_MDL_KEY;
125        // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
126        private String                  dbid            = null;
127        private String                  command         = CMD_ENTRY;
128        private String                  sql                     = null;
129        private String                  names           = null;
130        private String                  from            = null;
131        private String                  where           = null;
132        private String                  exist           = "auto";
133        private boolean                 errRemove       = false;
134
135        /**
136         * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
137         *
138         * @og.rev 3.7.1.0 (2005/04/15) notuse 値を追加
139         *
140         * @return      後続処理の指示(SKIP_BODY)
141         */
142        @Override
143        public int doStartTag() {
144                if( !"notuse".equalsIgnoreCase( exist ) ) {
145                        table = (DBTableModel)getObject( tableId );
146                        if( table != null && table.getRowCount() > 0 && check( command, COMMAND_LIST ) ) {
147                                sql = makeSQLString();
148                                execute( sql );
149                        }
150                }
151
152                return SKIP_BODY ;                              // Body を評価しない
153        }
154
155        /**
156         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
157         *
158         * @og.rev 3.5.4.2 (2003/12/15) HTMLTableViewForm クラス名変更(⇒ ViewForm_HTMLTable)
159         * @og.rev 3.5.4.4 (2004/01/16) エラー結果を表示するテーブル形式のフォーム修正
160         * @og.rev 3.5.5.2 (2004/04/02) TaglibUtil.makeHTMLErrorTable メソッドを利用
161         *
162         * @return      後続処理の指示
163         */
164        @Override
165        public int doEndTag() {
166                debugPrint();           // 4.0.0 (2005/02/28)
167
168                int rtnCode = EVAL_PAGE;
169
170                // 6.0.0.1 (2014/04/18) These nested if statements could be combined
171                if( CMD_ENTRY.equals( command ) && errMessage != null && ! errMessage.isOK() && !errRemove ) {
172                        rtnCode = SKIP_PAGE ;
173                        jspPrint( TaglibUtil.makeHTMLErrorTable( errMessage,getResource() ) );
174                }
175
176                return rtnCode ;
177        }
178
179        /**
180         * タグリブオブジェクトをリリースします。
181         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
182         *
183         * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更
184         */
185        @Override
186        protected void release2() {
187                super.release2();
188                tableId                 = HybsSystem.TBL_MDL_KEY;
189                dbid                    = null;
190                command                 = CMD_ENTRY;
191                table                   = null;
192                sql                             = null;
193                names                   = null;
194                errMessage              = null;
195                exist                   = "auto";
196                errRemove               = false;
197        }
198
199        /**
200         * SQL文を構築します。
201         *
202         * @return 構築された、SQL文
203         */
204        private String makeSQLString() {
205                StringBuilder rtn = new StringBuilder( HybsSystem.BUFFER_MIDDLE );
206                rtn.append( "select count(*) from " );
207                rtn.append( from );
208                rtn.append( " where " );
209                rtn.append( where );
210
211                return rtn.toString();
212        }
213
214        /**
215         * Query を実行します。
216         *
217         * @param   sql 検索文字列
218         *
219         * @og.rev 3.8.6.0 (2006/09/29) exist 属性の one を 「one:ひとつのみ」から「one:ひとつ以下」に変更
220         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
221         * @og.rev 4.0.0.0 (2005/01/31) getParameterRows() を使用するように変更
222         * @og.rev 4.0.0.0 (2007/11/28) 論理処理の不具合修正(カッコの付ける位置間違い)
223         */
224        private void execute( final String sql ) {
225                errMessage = new ErrorMessage( "Database Exist Data Error!" );
226
227                final String query ;
228                final int[]  clmNo ;
229
230                int[] rowNo = getParameterRows();               // 4.0.0 (2005/01/31)
231                if( rowNo.length == 0 ) { return; }
232
233                // names なしの場合は、Query より取得する。  4.0.0 (2005/01/31)
234                if( names == null ) {
235                        Formatter format = new Formatter( table );
236                        format.setFormat( sql );
237                        query = format.getQueryFormatString();
238                        clmNo = format.getClmNos();
239                        names = StringUtil.array2csv( table.getNames() );
240                }
241                else {
242                        clmNo = getTableColumnNo( StringUtil.csv2Array( names ) );
243                        query = sql;
244                }
245
246                int row;
247                boolean okFlag ;
248                List<Integer> list = new ArrayList<Integer>();
249                String[] values ;
250                for( int j=0; j<rowNo.length; j++ ) {
251                        okFlag = true;
252                        row = rowNo[j];
253                        values = getTableModelData( clmNo,row );
254                        int cnt = DBUtil.dbExist( query,values,getApplicationInfo(),dbid );
255
256                        String modifyType = table.getModifyType( row );
257                        if( ( "true".equalsIgnoreCase( exist ) ||
258                                ( "auto".equalsIgnoreCase( exist ) && (
259                                                DBTableModel.UPDATE_TYPE.equals( modifyType ) ||
260                                                DBTableModel.DELETE_TYPE.equals( modifyType ) ) ) ) && cnt <= 0 ) {
261                                // ERR0025=データ未登録エラー。キー={0}、値={1} のデータは、存在していません。
262                                String vals = StringUtil.array2csv( values );
263                                errMessage.addMessage( row,ErrorMessage.NG,"ERR0025",names,vals );
264                                okFlag = false;
265                        }
266                        // 4.0.0.0 (2007/11/28) 論理処理の不具合修正(カッコの付ける位置間違い)
267                        else if( ( "false".equalsIgnoreCase( exist ) ||
268                                         ( "auto".equalsIgnoreCase( exist ) &&
269                                                        DBTableModel.INSERT_TYPE.equals( modifyType ) ) ) && cnt > 0 ) {
270                                // ERR0026=データ登録済みエラー。キー={0}、値={1} のデータは、すでに存在しています。
271                                String vals = StringUtil.array2csv( values );
272                                errMessage.addMessage( row,ErrorMessage.NG,"ERR0026",names,vals );
273                                okFlag = false;
274                        }
275                        else if( "one".equalsIgnoreCase( exist ) && cnt > 1 ) {
276                                // ERR0027=データ2重登録エラー。キー={0}、値={1} のデータは、重複して存在しています。
277                                String vals = StringUtil.array2csv( values );
278                                errMessage.addMessage( row,ErrorMessage.NG,"ERR0027",names,vals );
279                                okFlag = false;
280                        }
281                        if( errRemove && okFlag ) { list.add( row ); }
282                }
283                if( errRemove ) {
284                        Integer[] in = list.toArray( new Integer[list.size()] );
285                        int[] newRowNo = new int[in.length];
286                        for( int i=0; i<in.length; i++ ) {
287                                newRowNo[i] = in[i].intValue();
288                        }
289                        setParameterRows( newRowNo );
290                }
291        }
292
293        /**
294         * 【廃止】(通常は使いません)結果をDBTableModelに書き込んで、sessionに登録するときのキーを指定します。
295         *
296         * @og.tag
297         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY}])。
298         *
299         * @param       id sessionに登録する時の ID
300         * @deprecated クラスが廃止されました。
301         */
302        @Deprecated public void setTableId( final String id ) {
303                tableId   = nval( getRequestParameter( id ),tableId );  // 3.8.0.9 (2005/10/17)
304        }
305
306        /**
307         * 【廃止】(通常は使いません)Queryオブジェクトを作成する時のDB接続IDを指定します。
308         *
309         * @og.tag Queryオブジェクトを作成する時のDB接続IDを指定します。
310         *
311         * @param       id データベース接続ID
312         * @deprecated クラスが廃止されました。
313         */
314        @Deprecated public void setDbid( final String id ) {
315                dbid = nval( getRequestParameter( id ),dbid );
316        }
317
318        /**
319         * 【廃止】コマンド(ENTRY)をセットします。
320         *
321         * @og.tag
322         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
323         * フィールド定数値のいづれかを、指定できます。
324         *
325         * @param       cmd コマンド(public static final 宣言されている文字列)
326         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.TableExistTag.CMD_NEW">コマンド定数</a>
327         * @deprecated クラスが廃止されました。
328         */
329        @Deprecated public void setCommand( final String cmd ) {
330                String cmd2 = getRequestParameter( cmd );
331                if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
332        }
333
334        /**
335         * 【廃止】引数にセットすべき データの名称(カラム名)をCSV形式で複数指定します。
336         *
337         * @og.tag
338         * 複数ある場合は、カンマ区切り文字で渡します。
339         * 引数をnames ではなく、[カラム名]形式で直接指定するほうが、SQL文が判りやすくなります。
340         *
341         * @param       nm 引数の名称(複数ある場合は、カンマ区切り文字)
342         * @deprecated クラスが廃止されました。
343         */
344        @Deprecated public void setNames( final String nm ) {
345                names = nval( getRequestParameter( nm ),names );
346        }
347
348        /**
349         * 【廃止】チェックするデータベース名(from 句)を指定します。
350         *
351         * @og.tag
352         * from 句 に指定するデータベース名です。
353         *
354         * @param       fm データベースID
355         * @deprecated クラスが廃止されました。
356         */
357        @Deprecated public void setFrom( final String fm ) {
358                from = nval( getRequestParameter( fm ),from );
359        }
360
361        /**
362         * 【廃止】チェックする検索条件(where句)を指定します。
363         *
364         * @og.tag
365         * where 区 に指定する検索条件です。? の部分に、names 属性で指定した
366         * カラムのデータが、DBTableModelより取り出されて適用されます。
367         * または、[カラム名]形式で、直接指定することもできます。その場合は、
368         * name 属性は指定する必要がありません。
369         * [カラム名]の前後に、(')シングルコーテーションは、不要です。
370         *
371         * @param       wr 検索条件(where句)
372         * @deprecated クラスが廃止されました。
373         */
374        @Deprecated public void setWhere( final String wr ) {
375                where = nval( getRequestParameter( wr ),where );
376        }
377
378        /**
379         * 【廃止】データベースのチェック方法(auto/true/false/one/notuse)を指定します(初期値:「auto:自動」)。
380         *
381         * @og.tag
382         * exist 属性に指定された 、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、
383         * の値は、いずれの場合も、成立時は、正常とみなします。
384         * 「auto:自動」は、DBTableModeleのmodifyType(A,C,D)に応じて、チェックします。
385         * A,C,D は、entryタグにコマンドを渡してデータを作成したときに、内部で作成されます。
386         * notuse は、チェックを行いません。これは、このタグを共有使用する場合に、外部で
387         * チェックを行うかどうかを指定できるようにするために使用します。
388         * (「true:存在する」 には、データが存在した場合に、OKで、なければエラーです。)
389         * 初期値は、「auto:自動」です。
390         *
391         * @param       ext チェック方法(「auto:自動」、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、「notuse:チェックしない」)
392         * @deprecated クラスが廃止されました。
393         */
394        @Deprecated public void setExist( final String ext ) {
395                exist = nval( getRequestParameter( ext ),exist );
396                if( !"auto".equalsIgnoreCase( exist ) &&
397                        !"true".equalsIgnoreCase( exist ) &&
398                        !"false".equalsIgnoreCase( exist ) &&
399                        !"one".equalsIgnoreCase( exist ) &&
400                        !"notuse".equalsIgnoreCase( exist ) ) {
401                                String errMsg = "exist 属性は、(auto,true,false,one,notuse)を指定してください。 [" + exist + "]" + HybsSystem.CR ;
402                                throw new HybsSystemException( errMsg );
403                }
404        }
405
406        /**
407         * 【廃止】エラー時の選択行を取り除いて継続処理を行うかどうか[true/false]を指定します(初期値:false)。
408         *
409         * @og.tag
410         * exist 属性に指定された 、「true:存在する」、「false:存在しない」、「one:ひとつ以下」、
411         * に対して、エラーが発生した選択行番号を、取り除いて以下の処理を継続するかどうかを
412         * 指定します。
413         * true に設定した場合は、エラーデータを削除し、継続処理を行うことができます。
414         * flase の場合は、エラーデータを表示して、継続処理を停止します。
415         * 初期値は、「false:エラー時停止」です。
416         *
417         * @param       flag エラー時の継続処理 [true:エラー行番号を取り除き継続処理/false:エラー時停止]
418         * @deprecated クラスが廃止されました。
419         */
420        @Deprecated public void setErrRemove( final String flag ) {
421                errRemove = nval( getRequestParameter( flag ),errRemove );
422        }
423
424        /**
425         *  カラム名配列(String[])より、対応するカラムNo配列(int[])を作成します。
426         *
427         * @param       nameArray カラム名配列
428         *
429         * @return      カラムNo配列
430         */
431        private int[] getTableColumnNo( final String[] nameArray ) {
432                int[] clmNo = new int[ nameArray.length ];
433                for( int i=0; i<clmNo.length; i++ ) {
434                        clmNo[i] = table.getColumnNo( nameArray[i] );
435                }
436                return clmNo;
437        }
438
439        /**
440         *  指定の行番号の、カラムNo配列(int[])に対応した値の配列を返します。
441         *
442         * 表示データの HybsSystem.ROW_SEL_KEY を元に、選ばれた 行を
443         * 処理の対象とします。
444         *
445         * @param       clmNo   カラムNo配列
446         * @param       row             行番号
447         *
448         * @return      行番号とカラムNo配列に対応した、値の配列
449         */
450        private String[] getTableModelData( final int[] clmNo,final int row ) {
451                String[] values = new String[ clmNo.length ];
452                for( int i=0; i<values.length; i++ ) {
453                        values[i] = table.getValue( row,clmNo[i] ) ;
454                }
455                return values;
456        }
457
458        /**
459         * シリアライズ用のカスタムシリアライズ書き込みメソッド
460         *
461         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
462         * @serialData 一部のオブジェクトは、シリアライズされません。
463         *
464         * @param       strm    ObjectOutputStreamオブジェクト
465         * @throws IOException  入出力エラーが発生した場合
466         */
467        private void writeObject( final ObjectOutputStream strm ) throws IOException {
468                strm.defaultWriteObject();
469        }
470
471        /**
472         * シリアライズ用のカスタムシリアライズ読み込みメソッド
473         *
474         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
475         *
476         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
477         * @serialData 一部のオブジェクトは、シリアライズされません。
478         *
479         * @param       strm    ObjectInputStreamオブジェクト
480         * @see #release2()
481         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
482         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
483         */
484        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
485                strm.defaultReadObject();
486        }
487
488        /**
489         * このオブジェクトの文字列表現を返します。
490         * 基本的にデバッグ目的に使用します。
491         *
492         * @return このクラスの文字列表現
493         */
494        @Override
495        public String toString() {
496                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
497                                .println( "VERSION"             ,VERSION        )
498                                .println( "tableId"             ,tableId        )
499                                .println( "dbid"                ,dbid           )
500                                .println( "command"             ,command        )
501                                .println( "sql"                 ,sql            )
502                                .println( "names"               ,names          )
503                                .println( "from"                ,from           )
504                                .println( "where"               ,where          )
505                                .println( "exist"               ,exist          )
506                                .println( "CMD_ENTRY"   ,CMD_ENTRY      )
507                                .println( "Other..."    ,getAttributes().getAttribute() )
508                                .fixForm().toString() ;
509        }
510}