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