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 // if( debug ) { println( arg.toString() ); } // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
146
147 if( infile1 == null || infile2 == null ) {
148 String errMsg = "ファイル名がæŒ?®šされてã�?�¾せんã€?
149 + "File1=[" + infile1 + "] , File2=[" + infile2 + "]" ;
150 throw new RuntimeException( errMsg );
151 }
152
153 File file1 = new File( infile1 );
154 File file2 = new File( infile2 );
155
156 if( ! file1.exists() || ! file2.exists() ) {
157 // 4.3.1.1 (2008/08/23) Avoid if (x != y) ..; else ..;
158 String errMsg = "ファイルが存在しませんã€?
159 + ((file1.exists()) ? "" : "File1=[" + file1 + "] " )
160 + ((file2.exists()) ? "" : "File2=[" + file2 + "]" );
161 throw new RuntimeException( errMsg );
162 }
163
164 if( ! file1.isFile() || ! file2.isFile() ) {
165 // 4.3.1.1 (2008/08/23) Avoid if (x != y) ..; else ..;
166 String errMsg = "フォルãƒ??æŒ?®šできません。ファイル名をæŒ?®šしてくださいã€?
167 + ((file1.isFile()) ? "" : "File1=[" + file1 + "] " )
168 + ((file2.isFile()) ? "" : "File2=[" + file2 + "]" );
169 throw new RuntimeException( errMsg );
170 }
171
172 reader1 = FileUtil.getBufferedReader( file1,encode1 );
173 // reader2 = FileUtil.getBufferedReader( file2,encode2 );
174
175 final String[] names ;
176 if( clms != null ) {
177 names = StringUtil.csv2Array( clms ); // æŒ?®šã?カラãƒ?��配å?
178 }
179 else {
180 String[] clmNames = readName( reader1 ); // ファイルのカラãƒ?��配å?
181 if( clmNames == null || clmNames.length == 0 ) { nameNull = true; return ; }
182 names = clmNames;
183 }
184
185 model = new LineModel();
186 model.init( names );
187
188 if( display ) { println( model.nameLine() ); }
189
190 // 入力カラãƒ?��のカラãƒ?•ª号
191 clmNos = new int[names.length];
192 for( int i=0; i<names.length; i++ ) {
193 clmNos[i] = i+1; // 行番号åˆ?‚’?‹1しておくã€?
194 // int no = model.getColumnNo( names[i] );
195 // if( no >= 0 ) { clmNos[no] = i+1; } // 行番号åˆ?‚’?‹1しておくã€?
196 }
197
198 // 比è¼?�™るå?の基準カラãƒ?��
199 if( debug ) { println( "DEBUG:\tkeyClms=" + keyClms ); }
200 final String[] keyClmNms = StringUtil.csv2Array( keyClms );
201 keyClmNos = new int[keyClmNms.length];
202 for( int i=0; i<keyClmNms.length; i++ ) {
203 keyClmNos[i] = model.getColumnNo( keyClmNms[i] );
204 // if( debug ) { println( "DEBUG:" + keyClmNms[i] + ":[" + keyClmNos[i] + "]" ); }
205 // int no = model.getColumnNo( keyClmNms[i] );
206 // if( no >= 0 ) { keyClmNos[no] = i+1; } // 行番号åˆ?‚’?‹1しておくã€?
207 }
208
209 // 比è¼?�™るカラãƒ?��
210 if( debug ) { println( "DEBUG:\tdiffClms=" + diffClms ); }
211 final String[] diffClmNms = StringUtil.csv2Array( diffClms );
212 diffClmNos = new int[diffClmNms.length];
213 for( int i=0; i<diffClmNms.length; i++ ) {
214 diffClmNos[i] = model.getColumnNo( diffClmNms[i] );
215 // if( debug ) { println( "DEBUG:" + diffClmNms[i] + ":[" + diffClmNos[i] + "]" ); }
216 // int no = model.getColumnNo( diffClmNms[i] );
217 // if( no >= 0 ) { diffClmNos[no] = i+1; } // 行番号åˆ?‚’?‹1しておくã€?
218 }
219
220 readF2Data( file2,encode2 );
221 }
222
223 /**
224 * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
225 * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç?に使用しますã?
226 *
227 * @param isOK トã?タルで、OKã�?�£たかどã�?�‹[true:成功/false:失敗]
228 */
229 public void end( final boolean isOK ) {
230 Closer.ioClose( reader1 );
231 reader1 = null;
232 }
233
234 /**
235 * こã?ãƒ??タの処ç�?�«おいて、次の処ç�?�Œ出来るかどã�?�‹を問ã�?�ˆわせますã?
236 * こã?呼び出し1回毎に、次のãƒ??タを取得する準備を行いますã?
237 *
238 * @return 処ç�?�§きる:true / 処ç�?�§きなã�?false
239 */
240 public boolean next() {
241 if( nameNull ) { return false; }
242
243 boolean flag = false;
244 try {
245 while((line = reader1.readLine()) != null) {
246 inCount1++ ;
247 if( line.length() == 0 || line.charAt( 0 ) == '#' ) { continue; }
248 else {
249 flag = true;
250 break;
251 }
252 }
253 }
254 catch (IOException ex) {
255 String errMsg = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ;
256 throw new RuntimeException( errMsg,ex );
257 }
258 return flag;
259 }
260
261 /**
262 * æœ??にã€?行データである LineModel を作æ?しまã�?
263 * FirstProcess は、次ã€?�¨処ç�?‚’チェインしてã�?��æœ??の行データã‚?
264 * 作æ?して、後続ã? ChainProcess クラスに処ç�?ƒ‡ータを渡しますã?
265 *
266 * ファイルより読み込んだ?‘行ã?ãƒ??タã‚?ãƒ??ブルモãƒ?ƒ«に
267 * セãƒ?ƒˆするようにåˆ?‰²しまã�?
268 * なおã?読込みは?ŒNAMEé ?›®åˆ?‚’読み込みますã?ãƒ??タ件数が少なã�??合ã?ã€?
269 * "" をセãƒ?ƒˆしておきますã?
270 *
271 * @param rowNo 処ç�?¸の行番号
272 *
273 * @return 処ç�?¤‰換後ã?LineModel
274 */
275 public LineModel makeLineModel( final int rowNo ) {
276 outCount++ ;
277 String[] vals = StringUtil.csv2Array( line ,separator1.charAt(0) );
278
279 int len = vals.length;
280 for( int clmNo=0; clmNo<model.size(); clmNo++ ) {
281 int no = clmNos[clmNo];
282 if( len > no ) {
283 model.setValue( clmNo,vals[no] );
284 }
285 else {
286 // EXCEL がã?終端TABを削除してしまã�?�Ÿめã?少なã�??合ã?埋めるã?
287 model.setValue( clmNo,"" );
288 }
289 }
290 model.setRowNo( rowNo ) ;
291
292 // if( display ) { println( model.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更
293
294 return action( model );
295 }
296
297 /**
298 * キーと、DIFF設定å?を比è¼?�—、action に応じã�?LineModel を返しますã?
299 * action には、ONLY,DIFF,INTERSEC が指定できますã?
300 * ONLY inFile1 のみに存在する行ã?場合ã?inFile1 のレコードを返しますã?
301 * DIFF inFile1 と inFile2 に存在しã?かつ、DIFF値が異なるã?inFile1 のレコードを返しますã?
302 * INTERSEC inFile1 と inFile2 に存在しã?かつ、DIFF値も同じã?inFile1 のレコードを返しますã?
303 * inFile2 側をキャãƒ?‚·ュしますã?で、inFile2 側のãƒ??タ量が少なã�?§˜に選んでくださいã€?
304 *
305 * @param model LineModelオブジェクãƒ?
306 *
307 * @return 実行後ã?LineModel
308 */
309 private LineModel action( final LineModel model ) {
310 LineModel rtn = null;
311 Object[] obj = model.getValues();
312
313 // キーのカラãƒ?‚’合æ?しますã?
314 StringBuilder keys = new StringBuilder();
315 for( int i=0; i<keyClmNos.length; i++ ) {
316 keys.append( obj[keyClmNos[i]] ).append( "," );
317 }
318
319 String data = file2Map.get( keys.toString() );
320 // if( debug ) { println( "DEBUG:" + keys.toString() + ":" + data ); }
321
322 if( "ONLY".equalsIgnoreCase( actCmnd ) && data == null ) {
323 if( debug ) { println( "DEBUG:ONLY\t" + keys.toString() ); }
324 rtn = model;
325 }
326 else {
327 // DIFF値のカラãƒ?‚’合æ?しますã?
328 StringBuilder vals = new StringBuilder();
329 for( int i=0; i<diffClmNos.length; i++ ) {
330 vals.append( obj[diffClmNos[i]] ).append( "," );
331 }
332
333 boolean eq = ( vals.toString() ).equals( data );
334
335 if( "DIFF".equalsIgnoreCase( actCmnd ) && ! eq ) {
336 if( debug ) { println( "DEBUG:DIFF\t" + keys.toString() + "\t" + data + "\t" + vals.toString() ); }
337 rtn = model;
338 }
339 else if( "INTERSEC".equalsIgnoreCase( actCmnd ) && eq ) {
340 if( debug ) { println( "DEBUG:INTERSEC\t" + keys.toString() + "\t" + data ); }
341 rtn = model;
342 }
343 }
344 if( display && rtn != null ) { println( rtn.dataLine() ); }
345 return rtn;
346 }
347
348 /**
349 * BufferedReader よりã€?NAME 行ã?é ?›®名情報を読み取りますã?
350 * ãƒ??タカラãƒ?‚ˆり前にã€??目名情報を示ã�?"#Name" が存在する仮定で取り込みますã?
351 * こã?行ã?、ファイルの形式に無関係に、TAB で区åˆ?‚‰れてã�?�¾すã?
352 *
353 * @param reader PrintWriterオブジェクãƒ?
354 *
355 * @return カラãƒ?��配å?(存在しなã�??合ã?、サイズ?�ã?配å?)
356 */
357 private String[] readName( final BufferedReader reader ) {
358 try {
359 // 4.0.0 (2005/01/31) line 変数名変更
360 String line1;
361 while((line1 = reader.readLine()) != null) {
362 inCount1++ ;
363 if( line1.length() == 0 ) { continue; }
364 if( line1.charAt(0) == '#' ) {
365 String key = line1.substring( 0,5 );
366 if( key.equalsIgnoreCase( "#NAME" ) ) {
367 // è¶?‚¤レギュラー処ç�?æœ??の TAB 以前ã?æ–?—ã?無視するã?
368 String line2 = line1.substring( line1.indexOf( TAB )+1 );
369 return StringUtil.csv2Array( line2 ,TAB.charAt(0) );
370 }
371 else { continue; }
372 }
373 else {
374 String errMsg = "#NAME が見つかる前にãƒ??タが見つかりましたã€?;
375 throw new RuntimeException( errMsg );
376 }
377 }
378 }
379 catch (IOException ex) {
380 String errMsg = "ファイル読込みエラー[" + infile1 + "]:(" + inCount1 + ")" ;
381 throw new RuntimeException( errMsg,ex );
382 }
383 return new String[0];
384 }
385
386 /**
387 * ファイル属æ?を読取り、キーæƒ??を作æ?しã?å†?ƒ¨メモリマップにキャãƒ?‚·ュしますã?
388 * こã?マップをもとに、inFile1 のãƒ??タをé?次読み取って、å?ç�?‚’進めますã?
389 *
390 * @param file2 読取りå…??ファイル
391 * @param encode2 ファイルのエンコーãƒ?
392 */
393 private void readF2Data( final File file2, final String encode2 ) {
394 BufferedReader reader2 = null;
395 try {
396 if( debug ) { println( "DEBUG:\tFile2="+ file2 + " 初期処ç�? ); }
397 reader2 = FileUtil.getBufferedReader( file2,encode2 );
398 // 4.0.0 (2005/01/31) line 変数名変更
399 String line1;
400 char sep2 = separator2.charAt(0);
401 while((line1 = reader2.readLine()) != null) {
402 inCount2++ ;
403 if( line1.length() == 0 ) { continue; }
404 if( line1.charAt(0) == '#' ) { continue; }
405 else {
406 // è¶?‚¤レギュラー処ç�?æœ??の TAB 以前ã?æ–?—ã?無視するã?
407 String line2 = line1.substring( line1.indexOf( separator2 )+1 );
408 Object[] obj = StringUtil.csv2Array( line2 , sep2 );
409
410 // キーのカラãƒ?‚’合æ?しますã?
411 StringBuilder keys = new StringBuilder();
412 for( int i=0; i<keyClmNos.length; i++ ) {
413 keys.append( obj[keyClmNos[i]] ).append( "," );
414 }
415
416 // DIFF値のカラãƒ?‚’合æ?しますã?
417 StringBuilder vals = new StringBuilder();
418 for( int i=0; i<diffClmNos.length; i++ ) {
419 vals.append( obj[diffClmNos[i]] ).append( "," );
420 }
421
422 if( debug ) { println( "DEBUG:\t" + keys.toString() + "\t" + vals.toString() ); }
423
424 file2Map.put( keys.toString(), vals.toString() );
425 }
426 }
427 if( debug ) { println( "DEBUG:\t======初期処ç�?µ‚äº?=====" ); }
428 }
429 catch (IOException ex) {
430 String errMsg = "ファイル読込みエラー[" + infile2 + "]:(" + inCount2 + ")" ;
431 throw new RuntimeException( errMsg,ex );
432 }
433 finally {
434 Closer.ioClose( reader2 );
435 }
436 }
437
438 /**
439 * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
440 * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
441 * こã?æ–?—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
442 * 形式で出してくださいã€?
443 *
444 * @return 処ç�?µ�果のレポã?ãƒ?
445 */
446 public String report() {
447 String report = "[" + getClass().getName() + "]" + CR
448 + TAB + "Input File1 : " + infile1 + CR
449 + TAB + "Input File2 : " + infile2 + CR
450 + TAB + "Input Count1 : " + inCount1 + CR
451 + TAB + "Input Count2 : " + inCount2 + CR
452 + TAB + "Output Count : " + outCount ;
453
454 return report ;
455 }
456
457 /**
458 * こã?クラスの使用方法を返しますã?
459 *
460 * @return こã?クラスの使用方æ³?
461 */
462 public String usage() {
463 StringBuilder buf = new StringBuilder();
464
465 buf.append( "Process_TableDiffは、ファイルから読み取ったå?容をã?LineModel に設定後ã?" ).append( CR );
466 buf.append( "下流に渡すã?FirstProcess インターフェースの実è£?‚¯ラスですã?" ).append( CR );
467 buf.append( CR );
468 buf.append( "DBTableModel 形式ã?ファイルを読み取って、各行を LineModel にセãƒ?ƒˆしてã€? ).append( CR );
469 buf.append( "下æµ?プロセスチェインのãƒ??タは上流から下流に渡されますã?)に渡しますã?" ).append( CR );
470 buf.append( CR );
471 buf.append( "引数æ–?—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??" ).append( CR );
472 buf.append( "引数æ–?—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に" ).append( CR );
473 buf.append( "繋げてくださいã€? ).append( CR );
474 buf.append( CR ).append( CR );
475
476 buf.append( getArgument().usage() ).append( CR );
477
478 return buf.toString();
479 }
480
481 /**
482 * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
483 *
484 * @param args コマンド引数配å?
485 */
486 public static void main( final String[] args ) {
487 LogWriter.log( new Process_TableDiff().usage() );
488 }
489 }