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 * これは、変更があったデータの行番号の配列をピックアップします。 675 * 676 * @og.rev 7.4.2.0 (2021/04/30) 変更があったデータのみを処理するかどうか[true/false]を指定します(初期値:false) 677 * 678 * @return 行番号の配列 679 */ 680 public int[] getChangeRowNos() { 681 final List<Integer> rows = new ArrayList<>(); 682 final int size = data.size() ; 683 for( int aRow=0; aRow<size; aRow++ ) { 684 final String[] dat = data.get(aRow); 685 final DBRowHeader head = rowHeader.get( aRow ); 686 final String[] obj = head.getBackupData(); 687 if( obj != null ) { 688 for( int aCol=0; aCol<numberOfColumns; aCol++ ) { 689 if( dat[aCol] != null && obj[aCol] != null && !dat[aCol].equals( obj[aCol] ) ) { 690 rows.add( aRow ); 691 break; 692 } 693 } 694 } 695 } 696 697 // List<Integer> を、int[] に変換します。 698 return rows.stream().mapToInt(i->i).toArray(); 699 } 700 701 /** 702 * 書込み許可を返します。 703 * 704 * @param aRow 値が参照される行 705 * 706 * @return 書込み可能(true)/不可能(false) 707 */ 708 public boolean isRowWritable( final int aRow ) { 709 final DBRowHeader row = rowHeader.get( aRow ); 710 return row.isWritable(); 711 } 712 713 /** 714 * 行が書き込み可能かどうかをセットします。 715 * デフォルト/およびなにも設定しない場合は, DEFAULT_WRITABLE が 716 * 与えられています。 717 * これが true の場合は,書込み許可です。(チェックボックスを表示) 718 * false の場合は,書込み不許可(チェックボックスは表示されません。) 719 * 720 * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。 721 * 722 * @param aRow 値が参照される行 723 * @param rw 書込み可能(true)/不可能(false) 724 */ 725 public void setRowWritable( final int aRow ,final boolean rw ) { 726 final DBRowHeader row = rowHeader.get( aRow ); 727 row.setWritable( rw ); 728 } 729 730 /** 731 * 書き込み可能な行(rowWritable == true)のチェックボックスに対して 732 * 初期値を 選択済みか、非選択済みかを返します。 733 * 734 * @param aRow 値が参照される行 735 * 736 * @return 初期値チェックON(true)/チェックOFF(false) 737 */ 738 public boolean isRowChecked( final int aRow ) { 739 final DBRowHeader row = rowHeader.get( aRow ); 740 return row.isChecked(); 741 } 742 743 /** 744 * 書き込み可能な行(rowWritable == true)のチェックボックスに対して 745 * 初期値を 選択済みにするか、非選択済みにするかを指定します。 746 * 747 * @og.rev 3.1.0.0 (2003/03/20) 同期メソッド(synchronized付き)を非同期に変更する。 748 * 749 * @param aRow 値が参照される行 750 * @param rw チェックON(true)/チェックOFF(false) 751 */ 752 public void setRowChecked( final int aRow ,final boolean rw ) { 753 final DBRowHeader row = rowHeader.get( aRow ); 754 row.setChecked( rw ); 755 } 756 757 /** 758 * 行指定の書込み許可を与えます。 759 * 具体的には,チェックボックスの表示/非表示を指定します。 760 * これが true の場合は,書込み許可です。(チェックボックスを表示) 761 * false の場合は,書込み不許可(チェックボックスは表示されません。) 762 * 行毎に書込み許可/不許可を指定する場合は,1カラム目に writable 763 * カラムを用意して true/false を指定します。 764 * この writable カラムとの論理積により最終的にチェックボックスの 765 * 表示の ON/OFF が決まります。 766 * なにも設定しない場合は, ViewForm.DEFAULT_WRITABLE が設定されます。 767 * 768 * @param rw 書込み可能(true)/不可能(false) 769 */ 770 public void setDefaultRowWritable( final boolean rw ) { 771 final int size = rowHeader.size() ; 772 DBRowHeader row ; 773 for( int i=0; i<size; i++ ) { 774 row = rowHeader.get( i ); 775 row.setWritable( rw ); 776 } 777 } 778 779 /** 780 * 書き込み可能な行(rowWritable == true)のチェックボックスに対して 781 * 初期値を 選択済みにするか、非選択済みにするかを指定します。 782 * 783 * @param rw 選択状態(true)/非選択状態(false) 784 */ 785 public void setDefaultRowChecked( final boolean rw ) { 786 final int size = rowHeader.size() ; 787 DBRowHeader row ; 788 for( int i=0; i<size; i++ ) { 789 row = rowHeader.get( i ); 790 row.setChecked( rw ); 791 } 792 } 793 794 /** 795 * 検索結果が オーバーフローしたかどうかをチェックします。 796 * Query で検索した場合に、DB_MAX_ROW_COUNT または、Query.setMaxRowCount( int maxRowCount ) 797 * で指定された値よりも検索結果が多い場合に、DBTableModel は、先の設定値までの 798 * データを取り込みます。そのときに、オーバーフローフラグを立てておくことで、最大件数を 799 * オーバーしたかどうかを判断します。 800 * 801 * @return オーバーフロー(true)/正常(false) 802 */ 803 public boolean isOverflow() { 804 return overflow; 805 } 806 807 /** 808 * 検索結果が オーバーフローしたかどうかを設定します。 809 * Query で検索した場合に、DB_MAX_ROW_COUNT または、Query.setMaxRowCount( int maxRowCount ) 810 * で指定された値よりも検索結果が多い場合に、DBTableModel は、先の設定値までの 811 * データを取り込みます。そのときに、オーバーフローフラグを立てておくことで、最大件数を 812 * オーバーしたかどうかを判断します。 813 * 814 * @param of オーバーフロー(true)/正常(false) 815 */ 816 public void setOverflow( final boolean of ) { 817 overflow = of; 818 } 819 820 /** 821 * 検索されたDBTableModelが登録時に同一かどうかを判断する為の 整合性キーを取得します。 822 * 823 * ここでの整合性は、同一セッション(ユーザー)毎にユニークかどうかで対応します。 824 * 分散環境(複数のセッション間)での整合性は、確保できません。 825 * 整合性キー は、オブジェクト作成時刻としますが、将来変更される可能性があります。 826 * 827 * @og.rev 3.5.5.5 (2004/04/23) 新規追加 828 * 829 * @return 整合性キー(オブジェクトの作成時刻) 830 */ 831 public String getConsistencyKey() { 832 return consistencyKey; 833 } 834 835 /** 836 * カラムに定義されたDBTypeよりNativeタイプを返します。 837 * Nativeタイプはorg.opengion.fukurou.model.NativeTypeで定義されています。 838 * 839 * @og.rev 4.1.1.2 (2008/02/28) 新規追加 840 * 841 * @param clm 値が参照される列 842 * 843 * @return Nativeタイプ 844 * @see org.opengion.fukurou.model.NativeType 845 */ 846 public NativeType getNativeType( final int clm ) { 847 return dbColumns[clm].getNativeType(); 848 } 849 850 /** 851 * カラム(列)にmustタイプ値を割り当てます。 852 * この値は、columnCheck 時の nullCheck や mustAnyCheck の 853 * チェック対象カラムとして認識されます。 854 * 855 * ※ 6.8.1.4 (2017/08/25) 856 * type に、null を指定できるようにします。その場合は、type="must" を 857 * 削除する動きになります。 858 * 本来なら、delMustType( int ) などのメソッド追加が良いのですが、 859 * 今回は、変更箇所を少ない目にするため、このメソッドのみで対応します。 860 * 861 * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録 862 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 863 * @og.rev 6.8.1.4 (2017/08/25) mustに、false 指定が出来るようにします。 864 * @og.rev 6.9.3.1 (2018/04/02) mustに、clear 指定で、mustMap すべてをクリアします。 865 * 866 * @param dbColumn カラムオブジェクト 867 * @param type mustタイプ(must,mustAny,false) 868 */ 869 public void addMustType( final int dbColumn, final String type ) { 870 if( "clear".equalsIgnoreCase( type ) ) { 871 mustMap.clear(); 872 } 873 else if( type != null && names[dbColumn] != null ) { 874 if( "false".equalsIgnoreCase( type ) ) { 875 mustMap.remove( "must" ); 876 } 877 else { 878 // たぶん、やりすぎ 879 // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし 880 mustMap.computeIfAbsent( type , k -> new HashSet<>() ).add( names[dbColumn] ); 881 } 882 } 883 } 884 885 /** 886 * mustType="must"時のカラム名を、文字列配列として返します。 887 * この値は、columnCheck 時の nullCheck のチェック対象カラムとして 888 * 認識されます。 889 * カラム名配列は、ソート済みです。 890 * 891 * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録 892 * 893 * @return mustType="must"時のカラム名配列(ソート済み) 894 */ 895 public String[] getMustArray() { 896 String[] rtn = null; 897 898 final Set<String> set = mustMap.get( "must" ); 899 if( set != null && ! set.isEmpty() ) { 900 rtn = set.toArray( new String[set.size()] ); 901 Arrays.sort( rtn ); 902 } 903 return rtn ; 904 } 905 906 /** 907 * mustType="mustAny" 他のカラム名を、文字列配列として返します。 908 * この値は、columnCheck 時の mustAnyCheck のチェック対象カラムとして 909 * 認識されます。 910 * カラム名配列は、ソート済みです。 911 * 912 * @og.rev 4.1.2.1 (2008/03/13) interface に新規登録 913 * 914 * @return mustType="mustAny"時のカラム名配列(ソート済み) 915 */ 916 public String[] getMustAnyArray() { 917 918 final List<String> list = new ArrayList<>(); 919 920 final String[] keys = mustMap.keySet().toArray( new String[mustMap.size()] ); 921 for( int i=0; i<keys.length; i++ ) { 922 final String key = keys[i]; 923 if( ! "must".equals( key ) ) { 924 final Set<String> set = mustMap.get( key ); 925 if( set != null && !set.isEmpty() ) { 926 final String str = StringUtil.iterator2line( set.iterator(),"|" ); 927 list.add( str ); 928 } 929 } 930 } 931 932 String[] rtn = null; 933 if( ! list.isEmpty() ) { 934 rtn = list.toArray( new String[list.size()] ); 935 Arrays.sort( rtn ); 936 } 937 938 return rtn ; 939 } 940}