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.db;
017
018import org.opengion.fukurou.util.StringUtil;
019import org.opengion.fukurou.model.NativeType;
020import org.opengion.hayabusa.common.HybsSystemException;
021import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
022
023import java.util.List;
024import java.util.ArrayList;
025import java.util.concurrent.ConcurrentMap;                                                      // 6.4.3.3 (2016/03/04)
026import java.util.concurrent.ConcurrentHashMap;                                          // 6.4.3.1 (2016/02/12) refactoring
027import java.util.Set;
028import java.util.HashSet;
029import java.util.Arrays;
030import java.util.Locale ;
031
032/**
033 * DBTableModel インターフェースを継承した TableModel の実装クラスです。
034 * sql文を execute( query ) する事により,データベースを検索した結果を
035 * DBTableModel に割り当てます。
036 *
037 * メソッドを宣言しています
038 * DBTableModel インターフェースは,データベースの検索結果(Resultset)をラップする
039 * インターフェースとして使用して下さい。
040 *
041 * @og.group テーブル管理
042 *
043 * @version  4.0
044 * @author   Kazuhiko Hasegawa
045 * @since    JDK5.0,
046 */
047public class DBTableModelImpl implements DBTableModel {
048        /** カラムオブジェクト配列 */
049        protected       DBColumn[]                      dbColumns       ;
050        /** カラム名称配列 */
051        protected       String[]                        names           ;
052        /** テータリスト */
053        protected       List<String[]>          data            ;
054        /** 行ヘッダー情報 */
055        protected       List<DBRowHeader>       rowHeader       ;
056        /**
057         * カラムアドレスマップ情報
058         * 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
059        */
060        protected       ConcurrentMap<String,Integer>   columnMap       ;
061        /** オーバーフローフラグ */
062        protected       boolean                         overflow        ;
063
064        /** カラム数 */
065        protected   int                 numberOfColumns         ;
066
067        // 3.5.5.5 (2004/04/23) 整合性キー(オブジェクトの作成時刻)追加
068        /** 整合性キー(オブジェクトの作成時刻) */
069        protected       String          consistencyKey  = String.valueOf( System.currentTimeMillis() );
070        private         String[]        lastData                ;
071        private         int             lastRow                 = -1;
072
073        // 4.1.2.1 (2008/03/13) カラム(列)にmustタイプ値を割り当てます。
074        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
075        private final   ConcurrentMap<String,Set<String>> mustMap = new ConcurrentHashMap<>() ; // 4.3.1.1 (2008/08/23) final化
076
077        /**
078         * デフォルトコンストラクター
079         *
080         * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
081         */
082        public DBTableModelImpl() { super(); }          // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
083
084        /**
085         * このオブジェクトを初期化します。
086         * 指定の引数分の内部配列を作成します。
087         *
088         * @og.rev 3.1.0.0 (2003/03/20) 実装を、Vector ,Hashtable から、ArrayList ,HashMapに、変更。
089         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
090         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
091         *
092         * @param   columnCount カラム数
093         */
094        public void init( final int columnCount ) {
095                data                    = new ArrayList<>( BUFFER_MIDDLE );
096                rowHeader               = new ArrayList<>( BUFFER_MIDDLE );
097                names                   = new String[columnCount];
098                dbColumns               = new DBColumn[ columnCount ];
099                numberOfColumns = columnCount;
100                columnMap               = new ConcurrentHashMap<>();    // 6.4.3.1 (2016/02/12)
101                lastRow                 = -1;                                                   // 3.5.5.7 (2004/05/10)
102        }
103
104        /**
105         * このオブジェクトをヘッダー部分をコピーし、データを初期化します。
106         * これは、カラムなどヘッダー系の情報は、元と同じオブジェクトを共有し、
107         * データ部のみ空にした DBTableModel を作成することを意味します。
108         * この際、consistencyKey も複写しますので、整合性は崩れないように、
109         * データ登録を行う必要があります。
110         *
111         * @og.rev 4.0.0.0 (2007/06/28) 新規作成
112         *
113         * @return  DBTableModelオブジェクト
114         */
115        public DBTableModel newModel() {
116                final DBTableModelImpl table = new DBTableModelImpl();
117
118                table.data                              = new ArrayList<>( BUFFER_MIDDLE );
119                table.rowHeader                 = new ArrayList<>( BUFFER_MIDDLE );
120                table.names                             = names;
121                table.dbColumns                 = dbColumns;
122                table.numberOfColumns   = numberOfColumns;
123                table.columnMap                 = columnMap;
124                table.lastRow                   = -1;
125                table.consistencyKey    = consistencyKey;
126
127                return table ;
128        }
129
130        /**
131         * カラム名配列を返します。
132         *
133         * @og.rev 3.0.0.0 (2002/12/25) カラム名配列を取得するメソッドを追加する。
134         * @og.rev 3.5.6.0 (2004/06/18) 配列をそのまま返さずに、clone して返します。
135         * @og.rev 3.6.0.0 (2004/09/22) names が null の場合は、初期設定エラーとします。
136         *
137         * @return      カラム名配列
138         * @og.rtnNotNull
139         */
140        public String[] getNames() {
141                if( names != null ) {
142                        return names.clone();
143                }
144
145                final String errMsg = "カラム名配列が、初期化されていません。";
146                throw new HybsSystemException( errMsg );
147        }
148
149        //////////////////////////////////////////////////////////////////////////
150        //
151        //   DBTableModelImpl 独自の実装部分
152        //
153        //////////////////////////////////////////////////////////////////////////
154
155        /**
156         * column に対応した 値を登録します。
157         * column には、番号ではなく、ラベルを指定します。
158         * 指定の行番号が、内部のデータ件数より多い場合は、データを追加します。
159         *
160         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
161         *
162         * @param   aRow    値が変更される行
163         * @param   columnName    値が変更されるカラム名
164         * @param   value   新しい値。null も可
165         */
166        public void setValue( final int aRow, final String columnName, final String value ) {
167                final int aColumn = getColumnNo( columnName );
168                final int size = getRowCount();
169                if( size > aRow ) {
170                        setRowHeader( aRow,UPDATE_TYPE );
171                        setValueAt( value , aRow, aColumn );
172                }
173                else {
174                        for( int i=0; i< (aRow-size)+1; i++ ) {
175                                final String[] columnValues = new String[numberOfColumns];
176                                Arrays.fill( columnValues,"" );                                 // 6.1.0.0 (2014/12/26) refactoring
177                                addColumnValues( columnValues );
178                        }
179                        setValueAt( value , aRow, aColumn );
180                }
181        }
182
183        /**
184         * 行を削除します。
185         * 物理削除ではなく、論理削除です。
186         * データを取り込むことは可能です。
187         *
188         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
189         *
190         * @param   aRow    論理削除される行
191         */
192        public void rowDelete( final int aRow ) {
193                setRowHeader( aRow,DELETE_TYPE );
194        }
195
196        /**
197         * row にあるセルのオブジェクト値を置き換えて、行を削除します。
198         * 物理削除ではなく、論理削除です。
199         * 値を置き換えたデータを取り込むことが可能です。
200         *
201         * @og.rev 3.5.4.2 (2003/12/15) 新規追加
202         *
203         * @param   values  新しい配列値。
204         * @param   aRow    論理削除される行
205         *
206         */
207        public void rowDelete( final String[] values, final int aRow ) {
208                if( numberOfColumns == values.length ) {                // 3.5.5.7 (2004/05/10)
209                        setRowHeader( aRow,DELETE_TYPE );
210                        data.set( aRow,values );
211                        lastRow = -1;                           // 3.5.5.7 (2004/05/10)
212                }
213                else {
214                        final String errMsg = "カラム名の個数が不一致です。 [" + numberOfColumns + "] : [" + values.length + "]"
215                                                                + " values=" + StringUtil.array2csv( values ) ;         // 5.1.8.0 (2010/07/01) errMsg 修正
216                        throw new HybsSystemException( errMsg );
217                }
218        }
219
220        /**
221         * 行を物理削除します。
222         * メモリ上で編集する場合に使用しますが,一般アプリケーションからの
223         * 使用は、物理削除の為,お勧めいたしません。
224         *
225         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
226         *
227         * @param   aRow    物理削除される行
228         *
229         */
230        public void removeValue( final int aRow ) {
231                data.remove( aRow );
232                rowHeader.remove( aRow );
233                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
234        }
235
236        //////////////////////////////////////////////////////////////////////////
237        //
238        //   DBTableModel インターフェースの実装部分
239        //
240        //////////////////////////////////////////////////////////////////////////
241
242        /**
243         * カラムのラベル名を返します。
244         * カラムの項目名に対して,見える形の文字列を返します。
245         * 一般には,リソースバンドルと組合せて,各国ロケール毎にラベルを
246         * 切替えます。
247         *
248         * @param   column カラム番号
249         *
250         * @return  カラムのラベル名
251         */
252        public String getColumnLabel( final int column ) {
253                return dbColumns[column].getLabel();
254        }
255
256        /**
257         * row および column にあるセルの属性値をStringに変換して返します。
258         *
259         * @og.rev 3.5.5.7 (2004/05/10) 連続同一 row アクセスのキャッシュ利用対応
260         *
261         * @param   aRow     値が参照される行
262         * @param   aColumn  値が参照される列
263         *
264         * @return  指定されたセルの値 String
265         */
266        public String getValue( final int aRow, final int aColumn ) {
267                if( aRow != lastRow ) {
268                        lastData = data.get(aRow);
269                        lastRow = aRow ;
270                }
271                return lastData[aColumn] ;
272        }
273
274        /**
275         * row および columnName にあるセルの属性値をStringに変換して返します。
276         *
277         * @param   aRow       値が参照される行
278         * @param   columnName 値が参照されるカラム名
279         *
280         * @return  指定されたセルの値 String
281         * @see #getValue( int , int )
282         */
283        public String getValue( final int aRow, final String columnName ) {
284                return getValue( aRow,getColumnNo( columnName ) );
285        }
286
287        /**
288         * カラム(列)にカラムオブジェクトを割り当てます。
289         * カラムオブジェクトは,ラベルやネームなど,そのカラム情報を
290         * 保持したオブジェクトです。
291         *
292         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
293         *
294         * @param   clm        ヘッダーを適応するカラム(列)
295         * @param   dbColumn   カラムオブジェクト
296         */
297        public void setDBColumn( final int clm, final DBColumn dbColumn ) {
298                dbColumns[clm] = dbColumn;
299                names[clm]     = dbColumn.getName();
300                columnMap.put( names[clm].toUpperCase(Locale.JAPAN),Integer.valueOf( clm ) );
301        }
302
303        /**
304         * カラム(列)のカラムオブジェクトを返します。
305         * カラムオブジェクトは,ラベルやネームなど,そのカラム情報を
306         * 保持したオブジェクトです。
307         *
308         * @param       clm     ヘッダーを適応するカラム(列)
309         *
310         * @return      カラムオブジェクト
311         */
312        public DBColumn getDBColumn( final int clm ) {
313                return dbColumns[ clm ];
314        }
315
316        /**
317         * カラムオブジェクト配列を返します。
318         * カラムオブジェクトは,ラベルやネームなど,そのカラム情報を
319         * 保持したオブジェクトです。
320         *
321         * @og.rev 4.0.0.0 (2005/12/31) 新規追加
322         *
323         * @return      カラムオブジェクト配列
324         */
325        public DBColumn[] getDBColumns() {
326                final int size = dbColumns.length;
327                final DBColumn[] clms = new DBColumn[size];
328                System.arraycopy( dbColumns,0,clms,0,size );
329                return clms;
330        }
331
332        /**
333         * カラム名をもとに、そのカラム番号を返します。
334         * カラム名が存在しない場合は、 HybsSystemException を throw します。
335         *
336         * @param   columnName   カラム名
337         *
338         * @return  カラム番号
339         * @see #getColumnNo( String ,boolean )
340         */
341        public int getColumnNo( final String columnName ) {
342                return getColumnNo( columnName,true );
343        }
344
345        /**
346         * カラム名をもとに、そのカラム番号を返します。
347         * useThrow が、true の場合は、カラム名が存在しない場合は、 HybsSystemException を
348         * throw します。useThrow が、false の場合は、カラム名が存在しない場合は、 -1 を返します。
349         *
350         * @og.rev 4.0.0.0 (2005/12/31) 新規追加
351         *
352         * @param   columnName   カラム名
353         * @param   useThrow     カラム名が存在しない場合に、Exception を throw するかどうか
354         *
355         * @return  カラム番号
356         * @see #getColumnNo( String )
357         */
358        public int getColumnNo( final String columnName,final boolean useThrow ) {
359                if( columnName != null ) {
360                        final Integer no = columnMap.get( columnName.toUpperCase(Locale.JAPAN) );
361                        if( no != null ) { return no.intValue() ; }
362                }
363
364                if( useThrow ) {
365                        final String errMsg = "カラム名が存在しません:[" + columnName + "]" ;
366                        throw new HybsSystemException( errMsg );
367                }
368                else {
369                        return -1;
370                }
371        }
372
373        //////////////////////////////////////////////////////////////////////////
374        //
375        //   DBTableModel クラスのオーバーライド部分
376        //
377        //////////////////////////////////////////////////////////////////////////
378
379        /**
380         * row の下に属性値配列を追加登録します。
381         *
382         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
383         *
384         * @param   values  属性値配列
385         * @param   aRow    値が参照される行
386         *
387         */
388        public void addValues( final String[] values ,final int aRow ) {
389                addValues( values, aRow, true ); // 4.3.1.0 (2008/09/04)
390        }
391
392        /**
393         * row の下に属性値配列を追加登録します。
394         * isWritableをfalseにした場合、編集不可能な状態で追加されます。
395         *
396         * @og.rev 4.3.1.0 (2008/09/04) interface に新規登録
397         *
398         * @param   values  属性値配列
399         * @param   aRow    値が参照される行
400         * @param   isWritable 編集不可能な状態で追加するか
401         *
402         */
403        public void addValues( final String[] values ,final int aRow, final boolean isWritable ) {
404                data.add( aRow,values );
405                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
406
407                final DBRowHeader rowhed = new DBRowHeader();
408                if( isWritable ) {
409                        rowhed.setType( INSERT_TYPE );
410                }
411                else {
412                        rowhed.setWritable( false );
413                        rowhed.setChecked( false );
414                }
415                rowHeader.add( aRow,rowhed );
416        }
417
418        /**
419         * row あるセルの属性値配列を追加登録します。
420         * これは,初期登録時のみに使用します。
421         *
422         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
423         *
424         * @param   values  属性値配列
425         */
426        public void addColumnValues( final String[] values ) {
427                data.add( values );
428                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
429                rowHeader.add( new DBRowHeader() );
430        }
431
432        /**
433         * row あるセルの属性値配列を追加登録します。
434         * これは,初期登録時のみに使用します。
435         * このメソッドでは、同時に、変更タイプ と、書込み許可を指定できます。
436         *
437         * @og.rev 6.2.2.0 (2015/03/27) interface に変更タイプ と、書込み許可を追加
438         *
439         * @param   values   属性値配列
440         * @param   modType  変更タイプ(追加/変更/削除)
441         * @param   rw 書込み可能(true)/不可能(false)
442         */
443        public void addColumnValues( final String[] values , final String modType , final boolean rw ) {
444                data.add( values );
445                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
446
447                final DBRowHeader rowhed = new DBRowHeader();
448                if( modType != null ) {
449                        rowhed.setType( modType );
450                }
451                rowhed.setWritable( rw );
452
453                rowHeader.add( rowhed );
454        }
455
456        //////////////////////////////////////////////////////////////////////////
457        //
458        //             Implementation of the TableModel Interface
459        //
460        //////////////////////////////////////////////////////////////////////////
461
462        // MetaData
463
464        /**
465         * カラム名を取得します。
466         *
467         * @param   column  最初のカラムは 0、2番目のカラムは 1、などとする。
468         *
469         * @return  カラム名
470         *
471         */
472        public String getColumnName( final int column ) {
473                return names[column];
474        }
475
476        /**
477         * データテーブル内の列の数を返します。
478         *
479         * @return  モデルの列数
480         *
481         */
482        public int getColumnCount() {
483                return numberOfColumns ;
484        }
485
486        /**
487         * データテーブル内の行の数を返します。
488         *
489         * @return  モデルの行数
490         *
491         */
492        public int getRowCount() {
493                return data.size() ;
494        }
495
496        /**
497         * column および row にあるセルのオブジェクト値を設定します。
498         * このメソッドは、行番号の範囲チェックや、列番号のチェックを行いません。
499         * また、登録に際して、更新マーカー(UPDATE_TYPE等)を設定しません。
500         *
501         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
502         * @og.rev 3.5.3.1 (2003/10/31) インターフェースの見直しにより、private 化する。
503         * @og.rev 4.0.0.0 (2007/05/24) インターフェースの見直しにより、public 化する。
504         *
505         * @param   value   新しい値。null も可
506         * @param   aRow    値が変更される行
507         * @param   aColumn 値が変更される列
508         */
509        public void setValueAt( final String value, final int aRow, final int aColumn ) {
510                String[] row = data.get(aRow);
511                row[ aColumn ] = value;
512                data.set( aRow,row );
513                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
514        }
515
516        //////////////////////////////////////////////////////////////////////////
517        //
518        //             DBTableModel 独自追加分
519        //
520        //////////////////////////////////////////////////////////////////////////
521
522        /**
523         * row にあるセルの属性値を配列で返します。
524         *
525         * @param   aRow     値が参照される行
526         *
527         * @return  指定されたセルの属性値
528         *
529         */
530        public String[] getValues( final int aRow ) {
531                return data.get(aRow);
532        }
533
534        /**
535         * row にあるセルのオブジェクト値を置き換えます。
536         *
537         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
538         *
539         * @param   values  新しい配列値。
540         * @param   aRow    値が変更される行
541         *
542         */
543        public void setValues( final String[] values, final int aRow ) {
544                if( numberOfColumns == values.length ) {                // 3.5.5.7 (2004/05/10)
545                        setRowHeader( aRow,UPDATE_TYPE );
546                        data.set( aRow,values );
547                        lastRow = -1;                           // 3.5.5.7 (2004/05/10)
548                }
549                else {
550                        final String errMsg = "カラム名の個数が不一致です。 [" + numberOfColumns + "] : [" + values.length + "]"
551                                                                + " values=" + StringUtil.array2csv( values ) ;         // 5.1.8.0 (2010/07/01) errMsg 修正
552                        throw new HybsSystemException( errMsg );
553                }
554        }
555
556        /**
557         * 変更済みフラグを元に戻します。
558         *
559         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
560         *
561         * 一般には,データベースにテーブルモデルを登録するタイミングで、
562         * 変更済みフラグを元に戻します。
563         *
564         */
565        public void resetModify() {
566                final int size = rowHeader.size() ;
567                DBRowHeader row ;
568                for( int i=0; i<size; i++ ) {
569                        row = rowHeader.get( i );
570                        row.clear();
571                }
572        }
573
574        /**
575         * 変更済みフラグを元に戻します。
576         *
577         * 一般には,データベースにテーブルモデルを登録するタイミングで、
578         * 変更済みフラグを元に戻します。
579         *
580         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
581         *
582         * @param   aRow     値が参照される行
583         */
584        public void resetModify( final int aRow ) {
585                final DBRowHeader row = rowHeader.get( aRow );
586                row.clear();
587        }
588
589        /**
590         * row 単位に変更されたタイプ(追加/変更/削除)を返します。
591         * タイプは始めに一度登録するとそれ以降に変更はかかりません。
592         * つまり、始めに 追加で作成したデータは、その後変更があっても追加のままです。
593         * なにも変更されていない場合は, ""(ゼロストリング)を返します。
594         *
595         * @param   aRow     値が参照される行
596         *
597         * @return  変更されたタイプの値 String
598         *
599         */
600        public String getModifyType( final int aRow ) {
601                final DBRowHeader row = rowHeader.get( aRow );
602                return row.getType();
603        }
604
605        /**
606         * row 単位に変更タイプ(追加/変更/削除)をセットします。
607         * このメソッドでは、データのバックアップは取りません。
608         * タイプは始めに一度登録するとそれ以降に変更はかかりません。
609         * なにも変更されていない場合は, ""(ゼロストリング)の状態です。
610         *
611         * @param   aRow     値が参照される行
612         * @param   modType  変更タイプ(追加/変更/削除)
613         *
614         */
615        public void setModifyType( final int aRow,final String modType ) {
616                final DBRowHeader rowhed = rowHeader.get( aRow );
617                rowhed.setType( modType );
618        }
619
620        /**
621         * row 単位に変更タイプ(追加/変更/削除)をセットします。
622         * セットすると同時に、データのバックアップを取ります。
623         * タイプは始めに一度登録するとそれ以降に変更はかかりません。
624         * つまり、始めに 追加で作成したデータは、その後変更があっても追加のままです。
625         * なにも変更されていない場合は, ""(ゼロストリング)の状態です。
626         *
627         * @og.rev 3.5.6.0 (2004/06/18) setBackupData 側で 配列をコピーしているため、こちらでは不要。
628         * @og.rev 3.5.6.4 (2004/07/16) protected 化します。
629         *
630         * @param   aRow     値が参照される行
631         * @param   modType  変更タイプ(追加/変更/削除)
632         */
633        protected void setRowHeader( final int aRow,final String modType ) {
634                final DBRowHeader rowhed = rowHeader.get( aRow );
635
636                rowhed.setBackupData( data.get(aRow) );
637                rowhed.setType( modType );
638        }
639
640        /**
641         * 変更データを初期値(元の取り込んだ状態)に戻します。
642         *
643         * 変更タイプ(追加/変更/削除)に応じて、処理されます。
644         * 追加時は、追加された行を削除します。
645         * 変更時は、変更された行を元に戻します。
646         * 削除時は、削除フラグを解除します。
647         * それ以外の場合(変更されていない場合)は、なにもしません。
648         *
649         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
650         * @og.rev 3.5.4.2 (2003/12/15) "DELETE" 時に値を置き換えた場合にUPDATEと同様に戻します。
651         *
652         * @param   aRow    処理を戻す(取り消す)行
653         */
654        public void resetRow( final int aRow ) {
655                final String modType = getModifyType(aRow) ;
656
657                if( modType.equals( INSERT_TYPE ) ) {
658                        data.remove( aRow );
659                        rowHeader.remove( aRow );
660                }
661                else if( modType.equals( UPDATE_TYPE ) ||
662                                 modType.equals( DELETE_TYPE ) ) {
663                        final DBRowHeader row = rowHeader.get( aRow );
664                        final String[] obj = row.getBackupData();
665                        if( obj != null ) { data.set( aRow,obj ); }
666                        row.clear();
667                }
668                lastRow = -1;                           // 3.5.5.7 (2004/05/10)
669        }
670
671        /**
672         * 書込み許可を返します。
673         *
674         * @param   aRow     値が参照される行
675         *
676         * @return  書込み可能(true)/不可能(false)
677         */
678        public boolean isRowWritable( final int aRow ) {
679                final DBRowHeader row = rowHeader.get( aRow );
680                return row.isWritable();
681        }
682
683        /**
684         * 行が書き込み可能かどうかをセットします。
685         * デフォルト/およびなにも設定しない場合は, DEFAULT_WRITABLE が
686         * 与えられています。
687         * これが true の場合は,書込み許可です。(チェックボックスを表示)
688         * false の場合は,書込み不許可(チェックボックスは表示されません。)
689         *
690         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
691         *
692         * @param   aRow     値が参照される行
693         * @param   rw 書込み可能(true)/不可能(false)
694         */
695        public void setRowWritable( final int aRow ,final boolean rw ) {
696                final DBRowHeader row = rowHeader.get( aRow );
697                row.setWritable( rw );
698        }
699
700        /**
701         * 書き込み可能な行(rowWritable == true)のチェックボックスに対して
702         * 初期値を 選択済みか、非選択済みかを返します。
703         *
704         * @param   aRow      値が参照される行
705         *
706         * @return      初期値チェックON(true)/チェックOFF(false)
707         */
708        public boolean isRowChecked( final int aRow ) {
709                final DBRowHeader row = rowHeader.get( aRow );
710                return row.isChecked();
711        }
712
713        /**
714         * 書き込み可能な行(rowWritable == true)のチェックボックスに対して
715         * 初期値を 選択済みにするか、非選択済みにするかを指定します。
716         *
717         * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。
718         *
719         * @param   aRow      値が参照される行
720         * @param   rw チェックON(true)/チェックOFF(false)
721         */
722        public void setRowChecked( final int aRow ,final boolean rw ) {
723                final DBRowHeader row = rowHeader.get( aRow );
724                row.setChecked( rw );
725        }
726
727        /**
728         * 行指定の書込み許可を与えます。
729         * 具体的には,チェックボックスの表示/非表示を指定します。
730         * これが true の場合は,書込み許可です。(チェックボックスを表示)
731         * false の場合は,書込み不許可(チェックボックスは表示されません。)
732         * 行毎に書込み許可/不許可を指定する場合は,1カラム目に writable
733         * カラムを用意して true/false を指定します。
734         * この writable カラムとの論理積により最終的にチェックボックスの
735         * 表示の ON/OFF が決まります。
736         * なにも設定しない場合は, ViewForm.DEFAULT_WRITABLE が設定されます。
737         *
738         * @param   rw 書込み可能(true)/不可能(false)
739         */
740        public void setDefaultRowWritable( final boolean rw ) {
741                final int size = rowHeader.size() ;
742                DBRowHeader row ;
743                for( int i=0; i<size; i++ ) {
744                        row = rowHeader.get( i );
745                        row.setWritable( rw );
746                }
747        }
748
749        /**
750         * 書き込み可能な行(rowWritable == true)のチェックボックスに対して
751         * 初期値を 選択済みにするか、非選択済みにするかを指定します。
752         *
753         * @param   rw 選択状態(true)/非選択状態(false)
754         */
755        public void setDefaultRowChecked( final boolean rw ) {
756                final int size = rowHeader.size() ;
757                DBRowHeader row ;
758                for( int i=0; i<size; i++ ) {
759                        row = rowHeader.get( i );
760                        row.setChecked( rw );
761                }
762        }
763
764        /**
765         * 検索結果が オーバーフローしたかどうかをチェックします。
766         * Query で検索した場合に、DB_MAX_ROW_COUNT または、Query.setMaxRowCount( int maxRowCount )
767         * で指定された値よりも検索結果が多い場合に、DBTableModel は、先の設定値までの
768         * データを取り込みます。そのときに、オーバーフローフラグを立てておくことで、最大件数を
769         * オーバーしたかどうかを判断します。
770         *
771         * @return   オーバーフロー(true)/正常(false)
772         */
773        public boolean isOverflow() {
774                return overflow;
775        }
776
777        /**
778         * 検索結果が オーバーフローしたかどうかを設定します。
779         * Query で検索した場合に、DB_MAX_ROW_COUNT または、Query.setMaxRowCount( int maxRowCount )
780         * で指定された値よりも検索結果が多い場合に、DBTableModel は、先の設定値までの
781         * データを取り込みます。そのときに、オーバーフローフラグを立てておくことで、最大件数を
782         * オーバーしたかどうかを判断します。
783         *
784         * @param   of オーバーフロー(true)/正常(false)
785         */
786        public void setOverflow( final boolean of ) {
787                overflow = of;
788        }
789
790        /**
791         * 検索されたDBTableModelが登録時に同一かどうかを判断する為の 整合性キーを取得します。
792         *
793         * ここでの整合性は、同一セッション(ユーザー)毎にユニークかどうかで対応します。
794         * 分散環境(複数のセッション間)での整合性は、確保できません。
795         * 整合性キー は、オブジェクト作成時刻としますが、将来変更される可能性があります。
796         *
797         * @og.rev 3.5.5.5 (2004/04/23) 新規追加
798         *
799         * @return   整合性キー(オブジェクトの作成時刻)
800         */
801        public String getConsistencyKey() {
802                return consistencyKey;
803        }
804
805        /**
806         * カラムに定義されたDBTypeよりNativeタイプを返します。
807         * Nativeタイプはorg.opengion.fukurou.model.NativeTypeで定義されています。
808         *
809         * @og.rev 4.1.1.2 (2008/02/28) 新規追加
810         *
811         * @param  clm      値が参照される列
812         *
813         * @return Nativeタイプ
814         * @see org.opengion.fukurou.model.NativeType
815         */
816        public NativeType getNativeType( final int clm ) {
817                return dbColumns[clm].getNativeType();
818        }
819
820        /**
821         * カラム(列)にmustタイプ値を割り当てます。
822         * この値は、columnCheck 時の nullCheck や mustAnyCheck の
823         * チェック対象カラムとして認識されます。
824         *
825         * ※ 6.8.1.4 (2017/08/25)
826         *    type に、null を指定できるようにします。その場合は、type="must" を
827         *    削除する動きになります。
828         *    本来なら、delMustType( int ) などのメソッド追加が良いのですが、
829         *    今回は、変更箇所を少ない目にするため、このメソッドのみで対応します。
830         *
831         * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録
832         * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
833         * @og.rev 6.8.1.4 (2017/08/25) mustに、false 指定が出来るようにします。
834         * @og.rev 6.9.3.1 (2018/04/02) mustに、clear 指定で、mustMap すべてをクリアします。
835         *
836         * @param   dbColumn  カラムオブジェクト
837         * @param   type      mustタイプ(must,mustAny,false)
838         */
839        public void addMustType( final int dbColumn, final String type ) {
840                if( "clear".equalsIgnoreCase( type ) ) {
841                                mustMap.clear();
842                }
843                else if( type != null && names[dbColumn] != null ) {
844                        if( "false".equalsIgnoreCase( type ) ) {
845                                mustMap.remove( "must" );
846                        }
847                        else {
848                                // たぶん、やりすぎ
849                                // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
850                                mustMap.computeIfAbsent( type , k -> new HashSet<>() ).add( names[dbColumn] );
851                        }
852                }
853        }
854
855        /**
856         * mustType="must"時のカラム名を、文字列配列として返します。
857         * この値は、columnCheck 時の nullCheck のチェック対象カラムとして
858         * 認識されます。
859         * カラム名配列は、ソート済みです。
860         *
861         * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録
862         *
863         * @return  mustType="must"時のカラム名配列(ソート済み)
864         */
865        public String[] getMustArray() {
866                String[] rtn = null;
867
868                final Set<String> set = mustMap.get( "must" );
869                if( set != null && ! set.isEmpty() ) {
870                        rtn = set.toArray( new String[set.size()] );
871                        Arrays.sort( rtn );
872                }
873                return rtn ;
874        }
875
876        /**
877         * mustType="mustAny" 他のカラム名を、文字列配列として返します。
878         * この値は、columnCheck 時の mustAnyCheck のチェック対象カラムとして
879         * 認識されます。
880         * カラム名配列は、ソート済みです。
881         *
882         * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録
883         *
884         * @return  mustType="mustAny"時のカラム名配列(ソート済み)
885         */
886        public String[] getMustAnyArray() {
887
888                final List<String> list = new ArrayList<>();
889
890                final String[] keys = mustMap.keySet().toArray( new String[mustMap.size()] );
891                for( int i=0; i<keys.length; i++ ) {
892                        final String key = keys[i];
893                        if( ! "must".equals( key ) ) {
894                                final Set<String> set = mustMap.get( key );
895                                if( set != null && !set.isEmpty() ) {
896                                        final String str = StringUtil.iterator2line( set.iterator(),"|" );
897                                        list.add( str );
898                                }
899                        }
900                }
901
902                String[] rtn = null;
903                if( ! list.isEmpty() ) {
904                        rtn = list.toArray( new String[list.size()] );
905                        Arrays.sort( rtn );
906                }
907
908                return rtn ;
909        }
910}