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 */ 016 package org.opengion.fukurou.process; 017 018 import org.opengion.fukurou.util.Argument; 019 import org.opengion.fukurou.util.StringUtil; 020 import org.opengion.fukurou.util.FileUtil; 021 import org.opengion.fukurou.util.Closer ; 022 import org.opengion.fukurou.util.LogWriter; 023 024 import java.util.Map ; 025 import java.util.HashMap ; 026 import java.util.LinkedHashMap ; 027 028 import java.io.File; 029 import java.io.BufferedReader; 030 import java.io.IOException; 031 032 /** 033 * Process_TableDiffは、ファイルから読み取った?容を?LineModel に設定後? 034 * 下流に渡す?FirstProcess インターフェースの実?ラスです? 035 * 036 * DBTableModel 形式?ファイルを読み取って、各行を LineModel にセ?して? 037 * 下?プロセスチェインの??タは上流から下流に渡されます?)に渡します? 038 * 039 * 引数??中にスペ?スを含??合?、ダブルコー??ション("") で括って下さ?? 040 * 引数??の ?』?前後には、スペ?スは挟めません。??key=value の様に 041 * 繋げてください? 042 * 043 * @og.formSample 044 * Process_TableDiff -infile1=INFILE -infile2=INFILE2 -action=DIFF1 -encode=UTF-8 -columns=AA,BB,CC 045 * 046 * -infile1=入力ファイル? ??力ファイル? 047 * -infile2=入力ファイル? ??力ファイル? 048 * -action=比?果の方? ?ONLY,DIFF,INTERSEC 049 * [-sep1=セパレータ?? ] ?区???(初期値:タ? 050 * [-sep2=セパレータ?? ] ?区???(初期値:タ? 051 * [-encode1=?エンコー? ] ??力ファイルのエンコードタイ? 052 * [-encode2=?エンコー? ] ??力ファイルのエンコードタイ? 053 * [-columns=読み取りカラ? ] ??力カラ?(カンマ区?) 054 * [-keyClms=比?るカラ? ] ?比?る?の基準カラ?(カンマ区?) 055 * [-diffClms=比?るカラ?] ?比?るカラ?(カンマ区?) 056 * [-display=false|true ] ?結果を標準?力に表示する(true)かしな?false)?初期値:false[表示しない]) 057 * [-debug=false|true ] ?デバッグ??を標準?力に表示する(true)かしな?false)?初期値:false[表示しない]) 058 * 059 * @og.rev 4.2.3.0 (2008/05/26) 新規作? 060 * 061 * @version 4.0 062 * @author Kazuhiko Hasegawa 063 * @since JDK5.0, 064 */ 065 public class Process_TableDiff extends AbstractProcess implements FirstProcess { 066 private static final String ENCODE = System.getProperty("file.encoding"); 067 068 private String separator1 = TAB; // ?区??? 069 private String separator2 = TAB; // ?区??? 070 private String infile1 = null; 071 private String infile2 = null; 072 private BufferedReader reader1 = null; 073 // private BufferedReader reader2 = null; 074 private LineModel model = null; 075 private String line = null; 076 private int[] clmNos = null; // ファイルのヘッ??のカラ?号 077 private int[] keyClmNos = null; // 比?る?の基準カラ?のカラ?号 078 private int[] diffClmNos = null; // 比?るカラ?のカラ?号 079 // private String action = null; 080 private String actCmnd = null; // action から名称変更 081 private boolean display = false; // 表示しな? 082 private boolean debug = false; // 表示しな? 083 private boolean nameNull = false; // ?件??タ?true 084 085 private final Map<String,String> file2Map = new HashMap<String,String>(); // 4.3.1.1 (2008/08/23) final? 086 087 private int inCount1 = 0; 088 private int inCount2 = 0; 089 private int outCount = 0; 090 091 private static final Map<String,String> mustProparty ; // ?プロパティ???チェ?用 Map 092 private static final Map<String,String> usableProparty ; // ?プロパティ?整合?チェ? Map 093 094 static { 095 mustProparty = new LinkedHashMap<String,String>(); 096 mustProparty.put( "infile1", "入力ファイル? (??)" ); 097 mustProparty.put( "infile2", "入力ファイル? (??)" ); 098 mustProparty.put( "action", "(??)ONLY,DIFF,INTERSEC" ); 099 mustProparty.put( "keyClms", "比?る?の基準カラ?(??)(カンマ区?)" ); 100 mustProparty.put( "diffClms", "比?るカラ?(??)(カンマ区?)" ); 101 102 usableProparty = new LinkedHashMap<String,String>(); 103 usableProparty.put( "sep1", "区??? (初期値:タ?" ); 104 usableProparty.put( "sep2", "区??? (初期値:タ?" ); 105 usableProparty.put( "encode1", "入力ファイルのエンコードタイ?" ); 106 usableProparty.put( "encode2", "入力ファイルのエンコードタイ?" ); 107 usableProparty.put( "columns", "入力カラ?(カンマ区?)" ); 108 usableProparty.put( "display", "結果を標準?力に表示する(true)かしな?false)? + 109 CR + " (初期値:false:表示しな?" ); 110 usableProparty.put( "debug", "????を標準?力に表示する(true)かしな?false)? + 111 CR + " (初期値:false:表示しな?" ); 112 } 113 114 /** 115 * ?ォルトコンストラクター? 116 * こ?クラスは、動??されます??ォルトコンストラクターで? 117 * super クラスに対して、?な初期化を行っておきます? 118 * 119 */ 120 public Process_TableDiff() { 121 super( "org.opengion.fukurou.process.Process_TableDiff",mustProparty,usableProparty ); 122 } 123 124 /** 125 * プロセスの初期化を行います?初めに??、呼び出されます? 126 * 初期処?ファイルオープン??オープン?に使用します? 127 * 128 * @param paramProcess ??タベ?スの接続???などを持って?オブジェク? 129 */ 130 public void init( final ParamProcess paramProcess ) { 131 Argument arg = getArgument(); 132 133 infile1 = arg.getProparty( "infile1" ); 134 infile2 = arg.getProparty( "infile2" ); 135 actCmnd = arg.getProparty( "action" ); 136 String encode1 = arg.getProparty( "encode1",ENCODE ); 137 String encode2 = arg.getProparty( "encode2",ENCODE ); 138 separator1 = arg.getProparty( "sep1",separator1 ); 139 separator2 = arg.getProparty( "sep2",separator2 ); 140 String clms = arg.getProparty( "columns" ); 141 String keyClms = arg.getProparty( "keyClms" ); 142 String diffClms = arg.getProparty( "diffClms" ); 143 display = arg.getProparty( "display",display ); 144 debug = arg.getProparty( "debug" ,debug ); 145 146 if( infile1 == null || infile2 == null ) { 147 String errMsg = "ファイル名が?されて?せん? 148 + "File1=[" + infile1 + "] , File2=[" + infile2 + "]" ; 149 throw new RuntimeException( errMsg ); 150 } 151 152 File file1 = new File( infile1 ); 153 File file2 = new File( infile2 ); 154 155 if( ! file1.exists() || ! file2.exists() ) { 156 // 4.3.1.1 (2008/08/23) Avoid if (x != y) ..; else ..; 157 String errMsg = "ファイルが存在しません? 158 + ((file1.exists()) ? "" : "File1=[" + file1 + "] " ) 159 + ((file2.exists()) ? "" : "File2=[" + file2 + "]" ); 160 throw new RuntimeException( errMsg ); 161 } 162 163 if( ! file1.isFile() || ! file2.isFile() ) { 164 // 4.3.1.1 (2008/08/23) Avoid if (x != y) ..; else ..; 165 String errMsg = "フォル???できません。ファイル名を?してください? 166 + ((file1.isFile()) ? "" : "File1=[" + file1 + "] " ) 167 + ((file2.isFile()) ? "" : "File2=[" + file2 + "]" ); 168 throw new RuntimeException( errMsg ); 169 } 170 171 reader1 = FileUtil.getBufferedReader( file1,encode1 ); 172 // reader2 = FileUtil.getBufferedReader( file2,encode2 ); 173 174 final String[] names ; 175 if( clms != null ) { 176 names = StringUtil.csv2Array( clms ); // ??カラ?配? 177 } 178 else { 179 String[] clmNames = readName( reader1 ); // ファイルのカラ?配? 180 if( clmNames == null || clmNames.length == 0 ) { nameNull = true; return ; } 181 names = clmNames; 182 } 183 184 model = new LineModel(); 185 model.init( names ); 186 187 if( display ) { println( model.nameLine() ); } 188 189 // 入力カラ?のカラ?号 190 clmNos = new int[names.length]; 191 for( int i=0; i<names.length; i++ ) { 192 clmNos[i] = i+1; // 行番号??1しておく? 193 // int no = model.getColumnNo( names[i] ); 194 // if( no >= 0 ) { clmNos[no] = i+1; } // 行番号??1しておく? 195 } 196 197 // 比?る?の基準カラ? 198 if( debug ) { println( "DEBUG:\tkeyClms=" + keyClms ); } 199 final String[] keyClmNms = StringUtil.csv2Array( keyClms ); 200 keyClmNos = new int[keyClmNms.length]; 201 for( int i=0; i<keyClmNms.length; i++ ) { 202 keyClmNos[i] = model.getColumnNo( keyClmNms[i] ); 203 // if( debug ) { println( "DEBUG:" + keyClmNms[i] + ":[" + keyClmNos[i] + "]" ); } 204 // int no = model.getColumnNo( keyClmNms[i] ); 205 // if( no >= 0 ) { keyClmNos[no] = i+1; } // 行番号??1しておく? 206 } 207 208 // 比?るカラ? 209 if( debug ) { println( "DEBUG:\tdiffClms=" + diffClms ); } 210 final String[] diffClmNms = StringUtil.csv2Array( diffClms ); 211 diffClmNos = new int[diffClmNms.length]; 212 for( int i=0; i<diffClmNms.length; i++ ) { 213 diffClmNos[i] = model.getColumnNo( diffClmNms[i] ); 214 // if( debug ) { println( "DEBUG:" + diffClmNms[i] + ":[" + diffClmNos[i] + "]" ); } 215 // int no = model.getColumnNo( diffClmNms[i] ); 216 // if( no >= 0 ) { diffClmNos[no] = i+1; } // 行番号??1しておく? 217 } 218 219 readF2Data( file2,encode2 ); 220 } 221 222 /** 223 * プロセスの終?行います??に??、呼び出されます? 224 * 終???ファイルクローズ??クローズ?に使用します? 225 * 226 * @param isOK ト?タルで、OK?たかど?[true:成功/false:失敗] 227 */ 228 public void end( final boolean isOK ) { 229 Closer.ioClose( reader1 ); 230 reader1 = null; 231 } 232 233 /** 234 * こ???タの処?おいて、次の処?出来るかど?を問?わせます? 235 * こ?呼び出し1回毎に、次の??タを取得する準備を行います? 236 * 237 * @return 処?きる:true / 処?きな?false 238 */ 239 public boolean next() { 240 if( nameNull ) { return false; } 241 242 boolean flag = false; 243 try { 244 while((line = reader1.readLine()) != null) { 245 inCount1++ ; 246 if( line.length() == 0 || line.charAt( 0 ) == '#' ) { continue; } 247 else { 248 flag = true; 249 break; 250 } 251 } 252 } 253 catch (IOException ex) { 254 String errMsg = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ; 255 throw new RuntimeException( errMsg,ex ); 256 } 257 return flag; 258 } 259 260 /** 261 * ??に?行データである LineModel を作?しま? 262 * FirstProcess は、次?処?チェインして???の行データ? 263 * 作?して、後続? ChainProcess クラスに処?ータを渡します? 264 * 265 * ファイルより読み込んだ?行???タ???ブルモ?に 266 * セ?するように?しま? 267 * なお?読込みは?NAME??読み込みます???タ件数が少な??合?? 268 * "" をセ?しておきます? 269 * 270 * @param rowNo 処?の行番号 271 * 272 * @return 処?換後?LineModel 273 */ 274 public LineModel makeLineModel( final int rowNo ) { 275 outCount++ ; 276 String[] vals = StringUtil.csv2Array( line ,separator1.charAt(0) ); 277 278 int len = vals.length; 279 for( int clmNo=0; clmNo<model.size(); clmNo++ ) { 280 int no = clmNos[clmNo]; 281 if( len > no ) { 282 model.setValue( clmNo,vals[no] ); 283 } 284 else { 285 // EXCEL が?終端TABを削除してしま?め?少な??合?埋める? 286 model.setValue( clmNo,"" ); 287 } 288 } 289 model.setRowNo( rowNo ) ; 290 291 // if( display ) { println( model.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 292 293 return action( model ); 294 } 295 296 /** 297 * キーと、DIFF設定?を比?、action に応じ?LineModel を返します? 298 * action には、ONLY,DIFF,INTERSEC が指定できます? 299 * ONLY inFile1 のみに存在する行?場合?inFile1 のレコードを返します? 300 * DIFF inFile1 と inFile2 に存在し?かつ、DIFF値が異なる?inFile1 のレコードを返します? 301 * INTERSEC inFile1 と inFile2 に存在し?かつ、DIFF値も同じ?inFile1 のレコードを返します? 302 * inFile2 側をキャ?ュします?で、inFile2 側の??タ量が少な?に選んでください? 303 * 304 * @param model LineModelオブジェク? 305 * 306 * @return 実行後?LineModel 307 */ 308 private LineModel action( final LineModel model ) { 309 LineModel rtn = null; 310 Object[] obj = model.getValues(); 311 312 // キーのカラ?合?します? 313 StringBuilder keys = new StringBuilder(); 314 for( int i=0; i<keyClmNos.length; i++ ) { 315 keys.append( obj[keyClmNos[i]] ).append( "," ); 316 } 317 318 String data = file2Map.get( keys.toString() ); 319 // if( debug ) { println( "DEBUG:" + keys.toString() + ":" + data ); } 320 321 if( "ONLY".equalsIgnoreCase( actCmnd ) && data == null ) { 322 if( debug ) { println( "DEBUG:ONLY\t" + keys.toString() ); } 323 rtn = model; 324 } 325 else { 326 // DIFF値のカラ?合?します? 327 StringBuilder vals = new StringBuilder(); 328 for( int i=0; i<diffClmNos.length; i++ ) { 329 vals.append( obj[diffClmNos[i]] ).append( "," ); 330 } 331 332 boolean eq = ( vals.toString() ).equals( data ); 333 334 if( "DIFF".equalsIgnoreCase( actCmnd ) && ! eq ) { 335 if( debug ) { println( "DEBUG:DIFF\t" + keys.toString() + "\t" + data + "\t" + vals.toString() ); } 336 rtn = model; 337 } 338 else if( "INTERSEC".equalsIgnoreCase( actCmnd ) && eq ) { 339 if( debug ) { println( "DEBUG:INTERSEC\t" + keys.toString() + "\t" + data ); } 340 rtn = model; 341 } 342 } 343 if( display && rtn != null ) { println( rtn.dataLine() ); } 344 return rtn; 345 } 346 347 /** 348 * BufferedReader より?NAME 行??名情報を読み取ります? 349 * ??タカラ?り前に??目名情報を示?"#Name" が存在する仮定で取り込みます? 350 * こ?行?、ファイルの形式に無関係に、TAB で区?れて?す? 351 * 352 * @param reader PrintWriterオブジェク? 353 * 354 * @return カラ?配?(存在しな??合?、サイズ??配?) 355 */ 356 private String[] readName( final BufferedReader reader ) { 357 try { 358 // 4.0.0 (2005/01/31) line 変数名変更 359 String line1; 360 while((line1 = reader.readLine()) != null) { 361 inCount1++ ; 362 if( line1.length() == 0 ) { continue; } 363 if( line1.charAt(0) == '#' ) { 364 String key = line1.substring( 0,5 ); 365 if( key.equalsIgnoreCase( "#NAME" ) ) { 366 // ?レギュラー処???の TAB 以前???無視する? 367 String line2 = line1.substring( line1.indexOf( TAB )+1 ); 368 return StringUtil.csv2Array( line2 ,TAB.charAt(0) ); 369 } 370 else { continue; } 371 } 372 else { 373 String errMsg = "#NAME が見つかる前に??タが見つかりました?; 374 throw new RuntimeException( errMsg ); 375 } 376 } 377 } 378 catch (IOException ex) { 379 String errMsg = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ; 380 throw new RuntimeException( errMsg,ex ); 381 } 382 return new String[0]; 383 } 384 385 /** 386 * ファイル属?を読取り、キー??を作?し??メモリマップにキャ?ュします? 387 * こ?マップをもとに、inFile1 の??タを?次読み取って、??進めます? 388 * 389 * @param file2 読取り??ファイル 390 * @param encode2 ファイルのエンコー? 391 */ 392 private void readF2Data( final File file2, final String encode2 ) { 393 BufferedReader reader2 = null; 394 try { 395 if( debug ) { println( "DEBUG:\tFile2="+ file2 + " 初期処? ); } 396 reader2 = FileUtil.getBufferedReader( file2,encode2 ); 397 // 4.0.0 (2005/01/31) line 変数名変更 398 String line1; 399 char sep2 = separator2.charAt(0); 400 while((line1 = reader2.readLine()) != null) { 401 inCount2++ ; 402 if( line1.length() == 0 ) { continue; } 403 if( line1.charAt(0) == '#' ) { continue; } 404 else { 405 // ?レギュラー処???の TAB 以前???無視する? 406 String line2 = line1.substring( line1.indexOf( separator2 )+1 ); 407 Object[] obj = StringUtil.csv2Array( line2 , sep2 ); 408 409 // キーのカラ?合?します? 410 StringBuilder keys = new StringBuilder(); 411 for( int i=0; i<keyClmNos.length; i++ ) { 412 keys.append( obj[keyClmNos[i]] ).append( "," ); 413 } 414 415 // DIFF値のカラ?合?します? 416 StringBuilder vals = new StringBuilder(); 417 for( int i=0; i<diffClmNos.length; i++ ) { 418 vals.append( obj[diffClmNos[i]] ).append( "," ); 419 } 420 421 if( debug ) { println( "DEBUG:\t" + keys.toString() + "\t" + vals.toString() ); } 422 423 file2Map.put( keys.toString(), vals.toString() ); 424 } 425 } 426 if( debug ) { println( "DEBUG:\t======初期処??=====" ); } 427 } 428 catch (IOException ex) { 429 String errMsg = "ファイル読込みエラー[" + infile2 + "]:(" + inCount2 + ")" ; 430 throw new RuntimeException( errMsg,ex ); 431 } 432 finally { 433 Closer.ioClose( reader2 ); 434 } 435 } 436 437 /** 438 * プロセスの処?果のレポ?ト表現を返します? 439 * 処??ログラ?、?力件数、?力件数などの??です? 440 * こ???をそのまま、標準?力に出すことで、結果レポ?トと出来るよ? 441 * 形式で出してください? 442 * 443 * @return 処?果のレポ?? 444 */ 445 public String report() { 446 String report = "[" + getClass().getName() + "]" + CR 447 + TAB + "Input File1 : " + infile1 + CR 448 + TAB + "Input File2 : " + infile2 + CR 449 + TAB + "Input Count1 : " + inCount1 + CR 450 + TAB + "Input Count2 : " + inCount2 + CR 451 + TAB + "Output Count : " + outCount ; 452 453 return report ; 454 } 455 456 /** 457 * こ?クラスの使用方法を返します? 458 * 459 * @return こ?クラスの使用方? 460 */ 461 public String usage() { 462 StringBuilder buf = new StringBuilder(); 463 464 buf.append( "Process_TableDiffは、ファイルから読み取った?容を?LineModel に設定後?" ).append( CR ); 465 buf.append( "下流に渡す?FirstProcess インターフェースの実?ラスです?" ).append( CR ); 466 buf.append( CR ); 467 buf.append( "DBTableModel 形式?ファイルを読み取って、各行を LineModel にセ?して? ).append( CR ); 468 buf.append( "下?プロセスチェインの??タは上流から下流に渡されます?)に渡します?" ).append( CR ); 469 buf.append( CR ); 470 buf.append( "引数??中に空白を含??合?、ダブルコー??ション(\"\") で括って下さ??" ).append( CR ); 471 buf.append( "引数??の ?』?前後には、空白は挟めません。??key=value の様に" ).append( CR ); 472 buf.append( "繋げてください? ).append( CR ); 473 buf.append( CR ).append( CR ); 474 475 buf.append( getArgument().usage() ).append( CR ); 476 477 return buf.toString(); 478 } 479 480 /** 481 * こ?クラスは、main メソ?から実行できません? 482 * 483 * @param args コマンド引数配? 484 */ 485 public static void main( final String[] args ) { 486 LogWriter.log( new Process_TableDiff().usage() ); 487 } 488 }