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 }