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 }