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.fukurou.process; 017 018import java.util.Map ; 019import java.util.concurrent.ConcurrentMap; // 6.4.3.4 (2016/03/11) 020import java.util.concurrent.ConcurrentHashMap; // 6.4.3.1 (2016/02/12) refactoring 021import java.util.LinkedHashMap ; 022import java.nio.charset.CharacterCodingException; // 6.3.1.0 (2015/06/28) 023 024import java.io.File; 025import java.io.BufferedReader; 026import java.io.IOException; 027 028import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 029import org.opengion.fukurou.system.OgCharacterException ; // 6.5.0.1 (2016/10/21) 030import org.opengion.fukurou.system.Closer ; 031import org.opengion.fukurou.system.LogWriter; 032import org.opengion.fukurou.util.Argument; 033import org.opengion.fukurou.util.StringUtil; 034import org.opengion.fukurou.util.FileUtil; 035 036/** 037 * Process_TableDiffは、ファイルから読み取った内容を、LineModel に設定後、 038 * 下流に渡す、FirstProcess インターフェースの実装クラスです。 039 * 040 * DBTableModel 形式のファイルを読み取って、各行を LineModel にセットして、 041 * 下流(プロセスチェインのデータは上流から下流に渡されます。)に渡します。 042 * 043 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。 044 * 引数文字列の 『=』 の前後には、スペースは挟めません。必ず、-key=value の様に 045 * 繋げてください。 046 * 047 * @og.formSample 048 * Process_TableDiff -infile1=INFILE -infile2=INFILE2 -action=DIFF1 -encode=UTF-8 -columns=AA,BB,CC 049 * 050 * -infile1=入力ファイル名1 :入力ファイル名1 051 * -infile2=入力ファイル名2 :入力ファイル名2 052 * -action=比較結果の方法 :ONLY,DIFF,INTERSEC 053 * [-sep1=セパレータ文字 ] :区切り文字1(初期値:タブ) 054 * [-sep2=セパレータ文字 ] :区切り文字2(初期値:タブ) 055 * [-encode1=文字エンコード ] :入力ファイルのエンコードタイプ1 056 * [-encode2=文字エンコード ] :入力ファイルのエンコードタイプ2 057 * [-columns=読み取りカラム名 ] :入力カラム名(CSV形式) 058 * [-keyClms=比較するカラム名 ] :比較する列の基準カラム名(CSV形式) 059 * [-diffClms=比較するカラム名] :比較するカラム名(CSV形式) 060 * [-display=[false/true] ] :結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 061 * [-debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 062 * 063 * @og.rev 4.2.3.0 (2008/05/26) 新規作成 064 * 065 * @version 4.0 066 * @author Kazuhiko Hasegawa 067 * @since JDK5.0, 068 */ 069public class Process_TableDiff extends AbstractProcess implements FirstProcess { 070 private static final String ENCODE = System.getProperty("file.encoding"); 071 072 private char separator1 = TAB; // 6.0.2.5 (2014/10/31) TAB を char 化 073 private char separator2 = TAB; // 6.0.2.5 (2014/10/31) TAB を char 化 074 private String infile1 ; 075 private String encode1 ; // 6.3.1.0 (2015/06/28) エラー時のメッセージに使う。 076 private String infile2 ; 077 private BufferedReader reader1 ; 078 private LineModel model ; 079 private String line ; 080 private int[] clmNos ; // ファイルのヘッダーのカラム番号 081 private int[] keyClmNos ; // 比較する列の基準カラム名のカラム番号 082 private int[] diffClmNos ; // 比較するカラム名のカラム番号 083 private String actCmnd ; // action から名称変更 084 private boolean display ; // 表示しない 085 private boolean debug ; // 表示しない 086 private boolean nameNull ; // 0件データ時 true 087 088 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 089 private final ConcurrentMap<String,String> file2Map = new ConcurrentHashMap<>(); // 4.3.1.1 (2008/08/23) final化 090 091 private int inCount1 ; 092 private int inCount2 ; 093 private int outCount ; 094 095 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 096 private static final Map<String,String> MUST_PROPARTY ; // [プロパティ]必須チェック用 Map 097 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 098 private static final Map<String,String> USABLE_PROPARTY ; // [プロパティ]整合性チェック Map 099 100 static { 101 MUST_PROPARTY = new LinkedHashMap<>(); 102 MUST_PROPARTY.put( "infile1", "入力ファイル名1 (必須)" ); 103 MUST_PROPARTY.put( "infile2", "入力ファイル名2 (必須)" ); 104 MUST_PROPARTY.put( "action", "(必須)ONLY,DIFF,INTERSEC" ); 105 MUST_PROPARTY.put( "keyClms", "比較する列の基準カラム名(必須)(CSV形式)" ); 106 MUST_PROPARTY.put( "diffClms", "比較するカラム名(必須)(CSV形式)" ); 107 108 USABLE_PROPARTY = new LinkedHashMap<>(); 109 USABLE_PROPARTY.put( "sep1", "区切り文字1 (初期値:タブ)" ); 110 USABLE_PROPARTY.put( "sep2", "区切り文字2 (初期値:タブ)" ); 111 USABLE_PROPARTY.put( "encode1", "入力ファイルのエンコードタイプ1" ); 112 USABLE_PROPARTY.put( "encode2", "入力ファイルのエンコードタイプ2" ); 113 USABLE_PROPARTY.put( "columns", "入力カラム名(CSV形式)" ); 114 USABLE_PROPARTY.put( "display", "結果を標準出力に表示する(true)かしない(false)か" + 115 CR + " (初期値:false:表示しない)" ); 116 USABLE_PROPARTY.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 117 CR + " (初期値:false:表示しない)" ); 118 } 119 120 /** 121 * デフォルトコンストラクター。 122 * このクラスは、動的作成されます。デフォルトコンストラクターで、 123 * super クラスに対して、必要な初期化を行っておきます。 124 * 125 */ 126 public Process_TableDiff() { 127 super( "org.opengion.fukurou.process.Process_TableDiff",MUST_PROPARTY,USABLE_PROPARTY ); 128 } 129 130 /** 131 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 132 * 初期処理(ファイルオープン、DBオープン等)に使用します。 133 * 134 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 135 * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: [this-escape] サブクラスが初期化される前の'this'エスケープの可能性があります 136 * 137 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 138 */ 139 public void init( final ParamProcess paramProcess ) { 140 final Argument arg = getArgument(); 141 142 infile1 = arg.getProparty( "infile1" ); 143 encode1 = arg.getProparty( "encode1",ENCODE ); 144 infile2 = arg.getProparty( "infile2" ); 145 actCmnd = arg.getProparty( "action" ); 146 display = arg.getProparty( "display",display ); 147 debug = arg.getProparty( "debug" ,debug ); 148 149 // 6.0.2.5 (2014/10/31) TAB を char 化 150 final String sep1 = arg.getProparty( "sep1",null ); 151 final String sep2 = arg.getProparty( "sep2",null ); 152 if( sep1 != null ) { separator1 = sep1.charAt(0); } 153 if( sep2 != null ) { separator2 = sep2.charAt(0); } 154 155 if( infile1 == null || infile2 == null ) { 156 final String errMsg = "ファイル名が指定されていません。" 157 + "File1=[" + infile1 + "] , File2=[" + infile2 + "]" ; 158 throw new OgRuntimeException( errMsg ); 159 } 160 161 final File file1 = new File( infile1 ); 162 final File file2 = new File( infile2 ); 163 164 if( ! file1.exists() || ! file2.exists() ) { 165 // 4.3.1.1 (2008/08/23) Avoid if(x != y) ..; else ..; 166 final String errMsg = "ファイルが存在しません。" 167 + ( file1.exists() ? "" : ( "File1=[" + file1 + "] " )) 168 + ( file2.exists() ? "" : ( "File2=[" + file2 + "]" )); 169 throw new OgRuntimeException( errMsg ); 170 } 171 172 if( ! file1.isFile() || ! file2.isFile() ) { 173 // 4.3.1.1 (2008/08/23) Avoid if(x != y) ..; else ..; 174 final String errMsg = "フォルダは指定できません。ファイル名を指定してください。" 175 + ( file1.isFile() ? "" : "File1=[" + file1 + "] " ) 176 + ( file2.isFile() ? "" : "File2=[" + file2 + "]" ); 177 throw new OgRuntimeException( errMsg ); 178 } 179 180 reader1 = FileUtil.getBufferedReader( file1,encode1 ); 181 182 final String[] names ; 183 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 184 final String clms = arg.getProparty( "columns" ); 185 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 186 if( clms == null ) { 187 final String[] clmNames = readName( reader1 ); // ファイルのカラム名配列 188 if( clmNames == null || clmNames.length == 0 ) { nameNull = true; return ; } 189 names = clmNames; 190 } 191 else { 192 names = StringUtil.csv2Array( clms ); // 指定のカラム名配列 193 } 194 195 model = new LineModel( names ); // 8.5.3.2 (2023/10/13) JDK21対応 196// model = new LineModel(); 197// model.init( names ); 198 199 if( display ) { println( model.nameLine() ); } 200 201 // 入力カラム名のカラム番号 202 clmNos = new int[names.length]; 203 for( int i=0; i<names.length; i++ ) { 204 clmNos[i] = i+1; // 行番号分を+1しておく。 205 } 206 207 // 比較する列の基準カラム名 208 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 209 final String keyClms = arg.getProparty( "keyClms" ); 210 if( debug ) { println( "DEBUG:\tkeyClms=" + keyClms ); } 211 final String[] keyClmNms = StringUtil.csv2Array( keyClms ); 212 keyClmNos = new int[keyClmNms.length]; 213 for( int i=0; i<keyClmNms.length; i++ ) { 214 keyClmNos[i] = model.getColumnNo( keyClmNms[i] ); 215 // if( debug ) { println( "DEBUG:" + keyClmNms[i] + ":[" + keyClmNos[i] + "]" ); } 216 // int no = model.getColumnNo( keyClmNms[i] ); 217 // if( no >= 0 ) { keyClmNos[no] = i+1; } // 行番号分を+1しておく。 218 } 219 220 // 比較するカラム名 221 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 222 final String diffClms = arg.getProparty( "diffClms" ); 223 if( debug ) { println( "DEBUG:\tdiffClms=" + diffClms ); } 224 final String[] diffClmNms = StringUtil.csv2Array( diffClms ); 225 diffClmNos = new int[diffClmNms.length]; 226 for( int i=0; i<diffClmNms.length; i++ ) { 227 diffClmNos[i] = model.getColumnNo( diffClmNms[i] ); 228 // if( debug ) { println( "DEBUG:" + diffClmNms[i] + ":[" + diffClmNos[i] + "]" ); } 229 // int no = model.getColumnNo( diffClmNms[i] ); 230 // if( no >= 0 ) { diffClmNos[no] = i+1; } // 行番号分を+1しておく。 231 } 232 233 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 234 final String encode2 = arg.getProparty( "encode2",ENCODE ); 235 readF2Data( file2,encode2 ); 236 } 237 238 /** 239 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 240 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 241 * 242 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 243 */ 244 public void end( final boolean isOK ) { 245 Closer.ioClose( reader1 ); 246 reader1 = null; 247 } 248 249 /** 250 * このデータの処理において、次の処理が出来るかどうかを問い合わせます。 251 * この呼び出し1回毎に、次のデータを取得する準備を行います。 252 * 253 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 254 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 255 * 256 * @return 処理できる:true / 処理できない:false 257 */ 258 @Override // FirstProcess 259 public boolean next() { 260 if( nameNull ) { return false; } 261 262 boolean flag = false; 263 try { 264 while((line = reader1.readLine()) != null) { 265 inCount1++ ; 266 if( line.isEmpty() || line.charAt(0) == '#' ) { continue; } 267 else { 268 flag = true; 269 break; 270 } 271 } 272 } 273 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 274 catch( final CharacterCodingException ex ) { 275 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 276 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 277 + " [" + infile1 + "] , Encode=[" + encode1 + "]" ; 278 throw new OgCharacterException( errMsg,ex ); // 6.5.0.1 (2016/10/21) 279 } 280 catch( final IOException ex) { 281 final String errMsg = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ; 282 throw new OgRuntimeException( errMsg,ex ); 283 } 284 return flag; 285 } 286 287 /** 288 * 最初に、行データである LineModel を作成します 289 * FirstProcess は、次々と処理をチェインしていく最初の行データを 290 * 作成して、後続の ChainProcess クラスに処理データを渡します。 291 * 292 * ファイルより読み込んだ1行のデータを テーブルモデルに 293 * セットするように分割します 294 * なお、読込みは、NAME項目分を読み込みます。データ件数が少ない場合は、 295 * "" をセットしておきます。 296 * 297 * @param rowNo 処理中の行番号 298 * 299 * @return 処理変換後のLineModel 300 */ 301 @Override // FirstProcess 302 public LineModel makeLineModel( final int rowNo ) { 303 outCount++ ; 304 final String[] vals = StringUtil.csv2Array( line ,separator1 ); // 6.0.2.5 (2014/10/31) TAB を char 化 305 306 final int len = vals.length; 307 for( int clmNo=0; clmNo<model.size(); clmNo++ ) { 308 final int no = clmNos[clmNo]; 309 if( len > no ) { 310 model.setValue( clmNo,vals[no] ); 311 } 312 else { 313 // EXCEL が、終端TABを削除してしまうため、少ない場合は埋める。 314 model.setValue( clmNo,"" ); 315 } 316 } 317 model.setRowNo( rowNo ) ; 318 319 // if( display ) { println( model.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 320 321 return action( model ); 322 } 323 324 /** 325 * キーと、DIFF設定値を比較し、action に応じた LineModel を返します。 326 * action には、ONLY,DIFF,INTERSEC が指定できます。 327 * ONLY inFile1 のみに存在する行の場合、inFile1 のレコードを返します。 328 * DIFF inFile1 と inFile2 に存在し、かつ、DIFF値が異なる、inFile1 のレコードを返します。 329 * INTERSEC inFile1 と inFile2 に存在し、かつ、DIFF値も同じ、inFile1 のレコードを返します。 330 * inFile2 側をキャッシュしますので、inFile2 側のデータ量が少ない様に選んでください。 331 * 332 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 333 * 334 * @param model LineModelオブジェクト 335 * 336 * @return 実行後のLineModel 337 */ 338 private LineModel action( final LineModel model ) { 339 LineModel rtn = null; 340 final Object[] obj = model.getValues(); 341 342 // キーのカラムを合成します。 343 final StringBuilder keys = new StringBuilder( BUFFER_MIDDLE ); 344 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 345// for( int i=0; i<keyClmNos.length; i++ ) { 346// keys.append( obj[keyClmNos[i]] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 347// } 348 for( final int keyNo : keyClmNos ) { 349 keys.append( obj[keyNo] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 350 } 351 352 final String data = file2Map.get( keys.toString() ); 353 // if( debug ) { println( "DEBUG:" + keys.toString() + ":" + data ); } 354 355 if( "ONLY".equalsIgnoreCase( actCmnd ) && data == null ) { 356 if( debug ) { println( "DEBUG:ONLY\t" + keys.toString() ); } 357 rtn = model; 358 } 359 else { 360 // DIFF値のカラムを合成します。 361 final StringBuilder vals = new StringBuilder( BUFFER_MIDDLE ); 362 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 363// for( int i=0; i<diffClmNos.length; i++ ) { 364// vals.append( obj[diffClmNos[i]] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 365// } 366 for( final int diffNo : diffClmNos ) { 367 vals.append( obj[diffNo] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 368 } 369 370 final boolean eq = vals.toString().equals( data ); 371 372 if( "DIFF".equalsIgnoreCase( actCmnd ) && ! eq ) { 373 if( debug ) { println( "DEBUG:DIFF\t" + keys.toString() + TAB + data + TAB + vals.toString() ); } 374 rtn = model; 375 } 376 else if( "INTERSEC".equalsIgnoreCase( actCmnd ) && eq ) { 377 if( debug ) { println( "DEBUG:INTERSEC\t" + keys.toString() + TAB + data ); } 378 rtn = model; 379 } 380 } 381 if( display && rtn != null ) { println( rtn.dataLine() ); } 382 return rtn; 383 } 384 385 /** 386 * BufferedReader より、#NAME 行の項目名情報を読み取ります。 387 * データカラムより前に、項目名情報を示す "#Name" が存在する仮定で取り込みます。 388 * この行は、ファイルの形式に無関係に、TAB で区切られています。 389 * 390 * @og.rev 6.0.4.0 (2014/11/28) #NAME 行の区切り文字は、指定の区切り文字を優先して利用する。 391 * @og.rev 6.0.4.0 (2014/11/28) #NAME 判定で、桁数不足のエラーが発生する箇所を修正。 392 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 393 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 394 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 395 * 396 * @param reader PrintWriterオブジェクト 397 * 398 * @return カラム名配列(存在しない場合は、サイズ0の配列) 399 * @og.rtnNotNull 400 */ 401 private String[] readName( final BufferedReader reader ) { 402 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 403 String errMsg = null; 404 405 try { 406 // 4.0.0 (2005/01/31) line 変数名変更 407 String line1; 408 while((line1 = reader.readLine()) != null) { 409 inCount1++ ; 410 if( line1.isEmpty() ) { continue; } 411 if( line1.charAt(0) == '#' ) { 412 // 6.0.4.0 (2014/11/28) #NAME 判定で、桁数不足のエラーが発生する箇所を修正。 413 if( line1.length() >= 5 && "#NAME".equalsIgnoreCase( line1.substring( 0,5 ) ) ) { 414 // 6.0.4.0 (2014/11/28) #NAME 行の区切り文字は、指定の区切り文字を優先して利用する。 415 final char sep ; 416 if( TAB != separator1 && line1.indexOf( separator1 ) >= 0 ) { 417 sep = separator1; 418 } 419 else { 420 sep = TAB; 421 } 422 // 超イレギュラー処理。#NAME をカラム列に入れない(#NAME+区切り文字 の 6文字分、飛ばす)。 423 return StringUtil.csv2Array( line1.substring( 6 ) ,sep ); 424 } 425 else { continue; } 426 } 427 else { 428 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 429// final String errMsg = "#NAME が見つかる前にデータが見つかりました。"; 430// throw new OgRuntimeException( errMsg ); 431 errMsg = "#NAME が見つかる前にデータが見つかりました。"; 432 break; 433 } 434 } 435 } 436 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 437 catch( final CharacterCodingException ex ) { 438 final String errMsg2 = "文字のエンコード・エラーが発生しました。" + CR 439 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 440 + " [" + infile1 + "] , Encode=[" + encode1 + "]" ; 441 throw new OgCharacterException( errMsg2,ex ); // 6.5.0.1 (2016/10/21) 442 } 443 catch( final IOException ex) { 444 final String errMsg2 = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ; 445 throw new OgRuntimeException( errMsg2,ex ); 446 } 447 448 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 449 if( errMsg != null ) { 450 throw new OgRuntimeException( errMsg ); 451 } 452 453 return new String[0]; 454 } 455 456 /** 457 * ファイル属性を読取り、キー情報を作成し、内部メモリマップにキャッシュします。 458 * このマップをもとに、inFile1 のデータを逐次読み取って、処理を進めます。 459 * 460 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 461 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 462 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 463 * 464 * @param file2 読取り元のファイル 465 * @param encode2 ファイルのエンコード 466 */ 467 private void readF2Data( final File file2, final String encode2 ) { 468 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 469// BufferedReader reader2 = null; 470// try { 471 try ( BufferedReader reader2 = FileUtil.getBufferedReader( file2,encode2 ) ) { 472 if( debug ) { println( "DEBUG:\tFile2="+ file2 + " 初期処理" ); } 473// reader2 = FileUtil.getBufferedReader( file2,encode2 ); 474 // 4.0.0 (2005/01/31) line 変数名変更 475 String line1; 476 final StringBuilder keys = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring 477 final StringBuilder vals = new StringBuilder( BUFFER_MIDDLE ); // 6.1.0.0 (2014/12/26) refactoring 478 while((line1 = reader2.readLine()) != null) { 479 inCount2++ ; 480 if( line1.isEmpty() || line1.charAt(0) == '#' ) { continue; } 481 else { 482 // 超イレギュラー処理 最初の TAB 以前の文字は無視する。 483 final String line2 = line1.substring( line1.indexOf( separator2 )+1 ); 484 final Object[] obj = StringUtil.csv2Array( line2 , separator2 ); // 6.0.2.5 (2014/10/31) TAB を char 化 485 486 // キーのカラムを合成します。 487 keys.setLength(0); // 6.1.0.0 (2014/12/26) refactoring 488 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 489// for( int i=0; i<keyClmNos.length; i++ ) { 490// keys.append( obj[keyClmNos[i]] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 491// } 492 for( final int keyNo : keyClmNos ) { 493 keys.append( obj[keyNo] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 494 } 495 496 // DIFF値のカラムを合成します。 497 vals.setLength(0); // 6.1.0.0 (2014/12/26) refactoring 498 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 499// for( int i=0; i<diffClmNos.length; i++ ) { 500// vals.append( obj[diffClmNos[i]] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 501// } 502 for( final int diffNo : diffClmNos ) { 503 vals.append( obj[diffNo] ).append( ',' ); // 6.0.2.5 (2014/10/31) char を append する。 504 } 505 506 if( debug ) { println( "DEBUG:\t" + keys.toString() + "\t" + vals.toString() ); } 507 508 file2Map.put( keys.toString(), vals.toString() ); 509 } 510 } 511 if( debug ) { println( "DEBUG:\t======初期処理終了======" ); } 512 } 513 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 514 catch( final CharacterCodingException ex ) { 515 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 516 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 517 + " [" + file2.getPath() + "] , Encode=[" + encode2 + "]" ; 518 throw new OgCharacterException( errMsg,ex ); // 6.5.0.1 (2016/10/21) 519 } 520 catch( final IOException ex) { 521 final String errMsg = "ファイル読込みエラー[" + file2.getPath() + "]:(" + inCount2 + ")" ; 522 throw new OgRuntimeException( errMsg,ex ); 523 } 524// finally { 525// Closer.ioClose( reader2 ); 526// } 527 } 528 529 /** 530 * プロセスの処理結果のレポート表現を返します。 531 * 処理プログラム名、入力件数、出力件数などの情報です。 532 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 533 * 形式で出してください。 534 * 535 * @return 処理結果のレポート 536 */ 537 public String report() { 538 // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX' 539 return "[" + getClass().getName() + "]" + CR 540// final String report = "[" + getClass().getName() + "]" + CR 541 + TAB + "Input File1 : " + infile1 + CR 542 + TAB + "Input File2 : " + infile2 + CR 543 + TAB + "Input Count1 : " + inCount1 + CR 544 + TAB + "Input Count2 : " + inCount2 + CR 545 + TAB + "Output Count : " + outCount ; 546 547// return report ; 548 } 549 550 /** 551 * このクラスの使用方法を返します。 552 * 553 * @return このクラスの使用方法 554 * @og.rtnNotNull 555 */ 556 public String usage() { 557 final StringBuilder buf = new StringBuilder( BUFFER_LARGE ) 558 .append( "Process_TableDiffは、ファイルから読み取った内容を、LineModel に設定後、" ).append( CR ) 559 .append( "下流に渡す、FirstProcess インターフェースの実装クラスです。" ).append( CR ) 560 .append( CR ) 561 .append( "DBTableModel 形式のファイルを読み取って、各行を LineModel にセットして、" ).append( CR ) 562 .append( "下流(プロセスチェインのデータは上流から下流に渡されます。)に渡します。" ).append( CR ) 563 .append( CR ) 564// .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ) 565// .append( "引数文字列の 『=』 の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ) 566// .append( "繋げてください。" ).append( CR ) 567 .append( PROCESS_PARAM_USAGE ) // 8.5.6.1 (2024/03/29) 継承元使用 568 .append( CR ).append( CR ) 569 .append( getArgument().usage() ).append( CR ); 570 571 return buf.toString(); 572 } 573 574 /** 575 * このクラスは、main メソッドから実行できません。 576 * 577 * @param args コマンド引数配列 578 */ 579 public static void main( final String[] args ) { 580 LogWriter.log( new Process_TableDiff().usage() ); 581 } 582}