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.FileString;
020 import org.opengion.fukurou.util.Closer ;
021 import org.opengion.fukurou.util.StringUtil ;
022 import org.opengion.fukurou.util.LogWriter;
023
024 import org.apache.poi.ss.usermodel.Cell;
025 import org.apache.poi.ss.usermodel.RichTextString;
026 import org.apache.poi.ss.usermodel.Row;
027 import org.apache.poi.ss.usermodel.Sheet;
028 import org.apache.poi.ss.usermodel.Workbook;
029 import org.apache.poi.ss.usermodel.WorkbookFactory;
030 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
031
032 import java.util.Map ;
033 import java.util.LinkedHashMap ;
034 import java.util.List ;
035 import java.util.ArrayList ;
036
037 import java.io.File;
038 import java.io.FileInputStream;
039 import java.io.FileOutputStream;
040 import java.io.IOException;
041
042 /**
043 * Process_GrepChangeExcel は、上流から受け取っ?FileLineModelから、語句?
044 * 置換する?ChainProcess インターフェースの実?ラスです?
045 *
046 * Process_GrepChange との違いは、?力?のファイルが??ストファイルなのか?
047 * ネイ?ブEXCELファイルなのか?違いです?
048 *
049 * keywordFile より、置換する語句を含?ーと値のペアー(タブ区?)を読取り?
050 * 対象とする語句をセル単位に置換します?
051 * keywordFile に、タブが含まれな?や、?頭にタブが存在して?場合??
052 * そ?行を読み飛?します?また?区?タブ?何?存在しても構いません?
053 * ただし?タブで区?た前(キー)と後ろ(値)は、trim() されます?で、スペ?ス
054 * が前後に存在して?場合?、ご注意く???
055 * 置換文?値)は、\t と \n の特殊文字が使用できます?
056 * こ? GrepChangeExcel では、語句に、正規表現は使用できません。正規表現のキーワー?
057 * ?字?を?行???と置き換える場合?、Process_Grep を使用してください?
058 * こ?プログラ?は、上流から受け取っ?FileLineModel のファイルに対して?
059 * 置き換えた結果も?同じファイルにセーブします?
060 * ??ファイルを保存したい場合?、予めバックア??を取得しておいてください?
061 * -inEncode は、keywordFileのエンコード指定になります?
062 * 初期値は、互換性を持つため、System.getProperty("file.encoding") ですが?
063 * 明示? UTF-8 などを指定して統?ておいたほ?良?しょ??
064 *
065 * 上流?ロセスでは、Name 属?として、?File』を持ち、?は、Fileオブジェク?
066 * である、Process_FileSearch を使用するのが?便利です?それ以外?クラス?
067 * 使用する場合でも?Name属?と、File オブジェクトを持つ LineModel を受け渡?
068 * できれば、使用可能です?
069 *
070 * 引数??中にスペ?スを含??合?、ダブルコー??ション("") で括って下さ??
071 * 引数??の ?』?前後には、スペ?スは挟めません。??key=value の様に
072 * 繋げてください?
073 *
074 * Process_GrepChangeExcel -keyword=検索?? -ignoreCase=true -outfile=OUTFILE -encode=UTF-8
075 *
076 * -keywordFile=キーワー? ?置換する語句を含?ーと値のペアー(タブ区?)
077 * [-ignoreCase=大?小文?] ?検索時に大?小文字を区別しな?true)かど?(初期値:false[区別する])
078 * [-isChange=置換可否 ] ?置換??実施する(true)かど?(初期値:true[置換する])
079 * [-inEncode=入力エンコー?] ?keywordFileのエンコー?
080 * [-display=false|true ] ?結果を標準?力に表示する(true)かしな?false)?初期値:false[表示しない])
081 * [-debug=false|true ] ?デバッグ用に実行?容を表示するかど?を指?初期値:false[表示しない])
082 *
083 * @og.rev 5.5.1.7 (2012/04/16) 新規追?
084 * @version 4.0
085 * @author Kazuhiko Hasegawa
086 * @since JDK5.0,
087 */
088 public class Process_GrepChangeExcel extends AbstractProcess implements ChainProcess {
089 private String[] keyword = null;
090 private String[] change = null;
091 private boolean ignoreCase = false;
092 private boolean isChange = true; // 5.1.2.0 (2010/01/01) 置換するかど?を指定可能にする
093 // private String inEncode = null; // 5.5.2.4 (2012/05/16) ローカル変数?
094 private boolean display = false; // 表示しな?
095 private boolean debug = false; // 表示しな?
096
097 private int inCount = 0;
098 private int findCount = 0;
099 private int cngCount = 0;
100
101 private static final Map<String,String> mustProparty ; // ?プロパティ???チェ?用 Map
102 private static final Map<String,String> usableProparty ; // ?プロパティ?整合?チェ? Map
103
104 static {
105 mustProparty = new LinkedHashMap<String,String>();
106 mustProparty.put( "keywordFile", "置換する語句を含?ーと値のペアー(タブ区?)(??)" );
107
108 usableProparty = new LinkedHashMap<String,String>();
109 usableProparty.put( "ignoreCase", "検索時に大?小文字を区別しな?true)かど?? +
110 CR + "(初期値:区別する[false])" );
111 usableProparty.put( "isChange", "置換??実施する(true)かど?" +
112 CR + "(初期値:置換する[true])" );
113 usableProparty.put( "inEncode", "keywordFileのエンコー? );
114 usableProparty.put( "display", "結果を標準?力に表示する(true)かしな?false)? +
115 CR + "(初期値:false:表示しな?" );
116 usableProparty.put( "debug", "??用に実行?容を表示するかど?を指? +
117 CR + "(初期値:false:表示しな?" );
118 }
119
120 /**
121 * ?ォルトコンストラクター?
122 * こ?クラスは、動??されます??ォルトコンストラクターで?
123 * super クラスに対して、?な初期化を行っておきます?
124 *
125 */
126 public Process_GrepChangeExcel() {
127 super( "org.opengion.fukurou.process.Process_GrepChangeExcel",mustProparty,usableProparty );
128 }
129
130 /**
131 * プロセスの初期化を行います?初めに??、呼び出されます?
132 * 初期処?ファイルオープン??オープン?に使用します?
133 *
134 * @param paramProcess ??タベ?スの接続???などを持って?オブジェク?
135 */
136 public void init( final ParamProcess paramProcess ) {
137 Argument arg = getArgument();
138
139 String keywordFile = arg.getProparty("keywordFile" );
140 ignoreCase = arg.getProparty("ignoreCase",ignoreCase);
141 isChange = arg.getProparty("isChange",isChange); // 5.1.2.0 (2010/01/01)
142 String inEncode = arg.getProparty("inEncode",System.getProperty("file.encoding"));
143 display = arg.getProparty("display",display);
144 debug = arg.getProparty("debug",debug);
145
146 FileString fs = new FileString();
147 fs.setFilename( keywordFile );
148 fs.setEncode( inEncode );
149 String[] lines = fs.getValue( "\n" );
150 int len = lines.length;
151 if( len == 0 ) {
152 String errMsg = "keywordFile の??読み取れませんでした?" + keywordFile + "]" ;
153 throw new RuntimeException( errMsg );
154 }
155
156 println( "keywordFile を?" + len + "件読み取りました? );
157 List<String> keyList = new ArrayList<String>( len );
158 List<String> cngList = new ArrayList<String>( len );
159
160 for( int i=0; i<len; i++ ) {
161 // String line = lines[i].trim();
162 String line = lines[i];
163 int indx = line.indexOf( '\t' );
164 if( indx <= 0 ) { continue ; } // TAB が?頭??存在しな??読み飛?す?
165 keyList.add( line.substring( 0,indx ).trim() );
166 String cng = line.substring( indx+1 ).trim();
167 cng = StringUtil.replace( cng,"\\n",CR );
168 cng = StringUtil.replace( cng,"\\t","\t" );
169 cngList.add( cng );
170 }
171 keyword = keyList.toArray( new String[keyList.size()] );
172 change = cngList.toArray( new String[cngList.size()] );
173 }
174
175 /**
176 * プロセスの終?行います??に??、呼び出されます?
177 * 終???ファイルクローズ??クローズ?に使用します?
178 *
179 * @param isOK ト?タルで、OK?たかど?[true:成功/false:失敗]
180 */
181 public void end( final boolean isOK ) {
182 // ここでは処?行いません?
183 }
184
185 /**
186 * 引数の LineModel を??るメソ?です?
187 * 変換処?? LineModel を返します?
188 * 後続??行わな?????タのフィルタリングを行う場?は?
189 * null ??タを返します?つまり?null ??タは、後続??行わな?
190 * フラグの代わりにも使用して?す?
191 * なお?変換処?? LineModel と、オリジナルの LineModel が?
192 * 同?、コピ?(クローン)か?、各処?ソ??決めて?す?
193 * ドキュメントに明記されて???合?、副作用が問題になる?合??
194 * ???とに自?コピ?(クローン)して下さ??
195 *
196 * @param data オリジナルのLineModel
197 *
198 * @return 処?換後?LineModel
199 */
200 public LineModel action( final LineModel data ) {
201 inCount++ ;
202 final FileLineModel fileData ;
203 if( data instanceof FileLineModel ) {
204 fileData = (FileLineModel)data ;
205 }
206 else {
207 String errMsg = "??タ?FileLineModel オブジェクトではありません? + CR ;
208 throw new RuntimeException( errMsg );
209 }
210
211 File org = fileData.getFile() ;
212 if( ! org.isFile() ) { return data; }
213
214 boolean nextFlag = false;
215
216 FileInputStream in = null;
217 Workbook wb = null;
218 Sheet sheet = null;
219 int stNo = -1 , rowNo = -1 , cellNo = -1 ; // エラー発生時に場?特定する為の??
220 String sheetName = null; // エラー発生時に場?特定する為の??
221 try {
222 in = new FileInputStream(org);
223 wb = WorkbookFactory.create(in); // HSSFとXSSFの違いをPOIが吸収してくれ?
224
225 for( stNo=0; stNo<wb.getNumberOfSheets(); stNo++ ) {
226 sheet = wb.getSheetAt(stNo);
227 sheetName = sheet.getSheetName();
228 if( display ) { println( org.getPath() + ":" + sheetName ); }
229
230 int nFirstRow = sheet.getFirstRowNum();
231 int nLastRow = sheet.getLastRowNum();
232 for( rowNo = nFirstRow; rowNo <= nLastRow; rowNo++) {
233 Row oRow = sheet.getRow(rowNo);
234 if( oRow == null ) { continue; }
235 int nFirstCell = oRow.getFirstCellNum();
236 int nLastCell = oRow.getLastCellNum();
237 for( cellNo = nFirstCell; cellNo <= nLastCell; cellNo++) {
238 Cell oCell = oRow.getCell( cellNo );
239 if( oCell != null ) {
240 int nCellType = oCell.getCellType();
241 // switch(nCellType) {
242 // case Cell.CELL_TYPE_STRING:
243 if( nCellType == Cell.CELL_TYPE_STRING ) {
244 RichTextString richText = oCell.getRichStringCellValue();
245 if( richText != null ) {
246 String orgText = richText.getString();
247 if( debug ) { println( "DEBUG: [" + rowNo + "," + cellNo + "]=" + orgText ); }
248
249 String strText = changeString( orgText ); // ??変換。無変換の場合?、null が返る?
250 if( strText != null ) {
251 if( display ) { println( "CHANGE: [" + rowNo + "," + cellNo + "]=" + orgText + "? + strText ); }
252 oCell.setCellValue( strText ); // Cell に書き戻?RichTextStringでな?大丈夫??
253 nextFlag = true;
254 findCount++; // 5.5.2.4 (2012/05/16)
255 }
256 }
257 // break;
258 // default :
259 // break;
260 }
261 }
262 }
263 }
264
265 // シート名も変換対象とする?
266 String newSheetName = changeString( sheetName ); // 無変換の場合?、null が返る?
267 if( newSheetName != null ) {
268 if( display ) { println( " sheetName=" + sheetName + "? + newSheetName ); }
269 wb.setSheetName(stNo, newSheetName);
270 nextFlag = true;
271 findCount++; // 5.5.2.4 (2012/05/16)
272 }
273 }
274 }
275 catch ( IOException ex ) {
276 String errMsg = "処?にエラーが発生しました?" + data.getRowNo() + "]件目" + CR
277 + org.toString() + CR
278 + "Sheet=[" + sheetName + "],SheetNo=[" + stNo + "],rowNo=[" + rowNo + "],cellNo=[" + cellNo + "]" ;
279 throw new RuntimeException( errMsg,ex );
280 }
281 catch ( InvalidFormatException ex ) {
282 String errMsg = "読み込みファイルの形式エラーが発生しました?" + data.getRowNo() + "]件目" + CR
283 + org.toString() + CR
284 + "Sheet=[" + sheetName + "],SheetNo=[" + stNo + "],rowNo=[" + rowNo + "],cellNo=[" + cellNo + "]" ;
285 throw new RuntimeException( errMsg,ex );
286 }
287 finally {
288 Closer.ioClose( in );
289 }
290
291 if( isChange && nextFlag ) {
292 FileOutputStream fileOut = null ;
293 try {
294 fileOut = new FileOutputStream( org );
295 wb.write(fileOut);
296 cngCount = findCount ; // 5.5.2.4 (2012/05/16) 置換時には、findCount を?cngCount にセ?しておく?
297 }
298 catch( IOException ex ) {
299 String errMsg = "ファイルへ書込み中にエラーが発生しました?" + data.getRowNo() + "]件目" + CR
300 + org.toString() ;
301 throw new RuntimeException( errMsg,ex );
302 }
303 finally {
304 Closer.ioClose( fileOut );
305 }
306 }
307
308 return (nextFlag) ? data : null ;
309 }
310
311 /**
312 * 引数の??から、keyword ファイルを?に??変換を行います?
313 *
314 * ここでは、変換が行われたかど?を判定するため?変換された??
315 * のみ、?を返します?変換されな??合?、null を返します?で?
316 * ご注意く???
317 *
318 * @param org 変換前???
319 *
320 * @return 変換後???(変換がなければ、null を返します?)
321 */
322 public String changeString( final String org ) {
323 if( org == null || org.isEmpty() ) { return null; }
324
325 String tgt = org;
326 for( int i=0; i<keyword.length; i++ ) {
327 tgt = tgt.replaceAll( keyword[i],change[i] );
328 }
329
330 // ?同じ場合?、null を返します?
331 if( org.equals( tgt ) || (ignoreCase && org.equalsIgnoreCase( tgt )) ) {
332 tgt = null;
333 }
334
335 return tgt ;
336 }
337
338 /**
339 * プロセスの処?果のレポ?ト表現を返します?
340 * 処??ログラ?、?力件数、?力件数などの??です?
341 * こ???をそのまま、標準?力に出すことで、結果レポ?トと出来るよ?
342 * 形式で出してください?
343 *
344 * @return 処?果のレポ??
345 */
346 public String report() {
347 String report = "[" + getClass().getName() + "]" + CR
348 + TAB + "Search File Count : " + inCount + CR
349 + TAB + "Key Find Count : " + findCount + CR
350 + TAB + "Key Change Count : " + cngCount ;
351
352 return report ;
353 }
354
355 /**
356 * こ?クラスの使用方法を返します?
357 *
358 * @return こ?クラスの使用方?
359 */
360 public String usage() {
361 StringBuilder buf = new StringBuilder();
362
363 buf.append( "Process_GrepChangeExcel は、上流から受け取っ?FileLineModelから、語句? ).append( CR );
364 buf.append( "置換する?ChainProcess インターフェースの実?ラスです?" ).append( CR );
365 buf.append( "Process_GrepChange との違いは、?力?のファイルが??ストファイルなのか?" ).append( CR );
366 buf.append( "ネイ?ブEXCELファイルなのか?違いです?" ).append( CR );
367 buf.append( CR );
368 buf.append( "keywordFile より、置換する語句を含?ーと値のペアー(タブ区?)を読取り? ).append( CR );
369 buf.append( "対象とする語句を置換します?" ).append( CR );
370 buf.append( "keywordFile に、タブが含まれな?や、?頭にタブが存在して?場合?? ).append( CR );
371 buf.append( "そ?行を読み飛?します?また?区?タブ?何?存在しても構いません? ).append( CR );
372 buf.append( "ただし?タブで区?た前(キー)と後ろ(値)は、trim() されます?で、スペ?ス" ).append( CR );
373 buf.append( "が前後に存在して?場合?、ご注意く???" ).append( CR );
374 buf.append( "置換文?値)は、\t と \n の特殊文字が使用できます?" ).append( CR );
375 buf.append( "こ? GrepChangeExcel では、語句に、正規表現は使用できません。正規表現のキーワー? ).append( CR );
376 buf.append( "?字?を?行???と置き換える場合?、Process_Grep を使用して下さ??" ).append( CR );
377 buf.append( "こ?プログラ?は、上流から受け取っ?FileLineModel のファイルに対して? ).append( CR );
378 buf.append( "置き換えた結果も?同じファイルにセーブします?" ).append( CR );
379 buf.append( "??ファイルを保存したい場合?、予めバックア??を取得しておいてください? ).append( CR );
380 buf.append( "-inEncode は、keywordFileのエンコード指定になります?" ).append( CR );
381 buf.append( "初期値は、互換性を持つため、System.getProperty(\"file.encoding\") ですが? ).append( CR );
382 buf.append( "明示? UTF-8 などを指定して統?ておいたほ?良?しょ??" ).append( CR );
383 buf.append( CR );
384 buf.append( "上流?ロセスでは、Name 属?として、?File』を持ち、?は、Fileオブジェク? ).append( CR );
385 buf.append( "である、Process_FileSearch を使用するのが?便利です?それ以外?クラス? ).append( CR );
386 buf.append( "使用する場合でも?Name属?と、File オブジェクトを持つ LineModel を受け渡? ).append( CR );
387 buf.append( "できれば、使用可能です?" ).append( CR );
388 buf.append( CR );
389 buf.append( "引数??中に空白を含??合?、ダブルコー??ション(\"\") で括って下さ??" ).append( CR );
390 buf.append( "引数??の ?』?前後には、空白は挟めません。??key=value の様に" ).append( CR );
391 buf.append( "繋げてください? ).append( CR );
392 buf.append( CR ).append( CR );
393
394 buf.append( getArgument().usage() ).append( CR );
395
396 return buf.toString();
397 }
398
399 /**
400 * こ?クラスは、main メソ?から実行できません?
401 *
402 * @param args コマンド引数配?
403 */
404 public static void main( final String[] args ) {
405 LogWriter.log( new Process_GrepChangeExcel().usage() );
406 }
407 }