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