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 */ 016package org.opengion.fukurou.process; 017 018import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import org.opengion.fukurou.system.OgCharacterException ; // 6.5.0.1 (2016/10/21) 020import org.opengion.fukurou.system.DateSet; // 6.4.2.0 (2016/01/29) 021import org.opengion.fukurou.system.Closer; 022import org.opengion.fukurou.util.FileUtil; 023import org.opengion.fukurou.util.CommentLineParser; 024import org.opengion.fukurou.util.FileInfo; // 6.4.0.2 (2015/12/11) 025import org.opengion.fukurou.security.HybsCryptography ; // 5.7.2.1 (2014/01/17) 026import static org.opengion.fukurou.system.HybsConst.CR; // 6.3.1.0 (2015/06/28) 027 028import java.io.File; 029import java.io.BufferedReader; 030import java.io.PrintWriter; 031import java.io.IOException; 032import java.nio.charset.CharacterCodingException; // 6.3.1.0 (2015/06/28) 033 034/** 035 * FileLineModel は、LineModel を継承した ファイルリスト専用の 036 * LineModel の実装クラスです。 037 * 038 * FileLineModel オブジェクトには、ファイル属性(Level,File,Length,Modify,LineCnt,Biko,MD5) 039 * が設定されます。 040 * オプションで、FILEPATH,ADDRESS,FILENAME 属性を文字列で準備できます。(6.3.1.0 (2015/06/28)) 041 * ADDRESS は、指定ファイルの親フォルダ。FILENAME はファイル名。FILEPATH は、ファイル名を含む 042 * 完全なファイルパスになります。 043 * ※ 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 044 * この、新しい属性に、値をセットする場合は、useFilePath="true" をセットしてください。 045 * ※ 6.3.1.1 (2015/07/10) Modify のフォーマット(modifyForm)を、指定可能にします。 046 * これは、Date型のまま、扱いたい所だが、文字列化しています。 047 * 初期値は、"yyyy/MM/dd HH:mm:ss" です。 048 * 049 * LineCnt と、MD5 は、それぞれ、計算するかどうかのフラグを設定する必要があります。 050 * 051 * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。 052 * 一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、 053 * 先のLength(文字数)値とは異なりますのでご注意ください。 054 * 055 * omitCmnt=true にすると、コメント部分を削除した行数と文字数を求めます。 056 * これは、/* から */ の間、// から改行までです。 057 * ただし、"(二重引用符)で囲まれた文字列は、コメントとみなしません。 058 * 059 * データの1行分を FileLineModel に割り当てます。 060 * カラム番号は、0 から始まります。カラム名よりカラム番号を求める場合に、 061 * 存在しない場合は、-1 を返します。 062 * カラム番号が -1 の場合は、処理を行いません。 063 * 064 * 注意:このクラスは、同期処理されていません。 065 * 066 * @version 4.0 067 * @author Kazuhiko Hasegawa 068 * @since JDK5.0, 069 */ 070public class FileLineModel extends LineModel { 071 // 5.7.2.1 (2014/01/17) MD5 項目追加 072 // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 073 private static final String[] KEYS = { "Level","File","Length","Modify","LineCnt","Biko","MD5","FILEPATH","ADDRESS","FILENAME" }; 074 075 private static final int LEVEL = 0; 076 private static final int FILE = 1; 077 private static final int LENGTH = 2; 078 private static final int MODIFY = 3; 079 private static final int LINECNT = 4; 080 private static final int BIKO = 5; 081 private static final int MD5 = 6; // 5.7.2.1 (2014/01/17) 082 private static final int FILEPATH = 7; // 6.3.1.0 (2015/06/28) 083 private static final int ADDRESS = 8; // 6.3.1.0 (2015/06/28) 084 private static final int FILENAME = 9; // 6.3.1.0 (2015/06/28) 085 086 private final boolean useLineCnt ; 087 private final boolean useMD5 ; // 5.7.2.1 (2014/01/17) MD5 項目追加 088 private final boolean omitCmnt ; // 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する) 089 private final boolean useFilePath ; // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性を使う場合は、true 090 private String encode = "JISAutoDetect"; // 5.7.4.0 (2014/03/07) コメント削除時の文字数計算で利用するファイルのエンコード 091 private String modifyForm = "yyyy/MM/dd HH:mm:ss" ; // 6.3.1.1 (2015/07/10) 092 093 /** 094 * コンストラクターです。 095 * useLineCnt=false , useMD5=false , omitCmnt=false で初期化されます。 096 * 097 * @og.rev 5.7.2.1 (2014/01/17) MD5対応 098 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応 099 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 100 * 101 */ 102 public FileLineModel() { 103 this( false,false,false,false ); // 6.3.1.0 (2015/06/28) 104 } 105 106 /** 107 * ラインカウントの有無を指定した、コンストラクターです。 108 * useMD5=false , omitCmnt=false で初期化されます。 109 * 110 * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無 111 * @og.rev 5.7.2.1 (2014/01/17) MD5対応 112 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応 113 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 114 * 115 * @param isLineCnt 行数カウントの使用有無 116 */ 117 public FileLineModel( final boolean isLineCnt ) { 118 this( isLineCnt,false,false,false ); // 6.3.1.0 (2015/06/28) 119 } 120 121 /** 122 * ラインカウントの有無と、MD5計算の有無を指定した、コンストラクターです。 123 * omitCmnt=false で初期化されます。 124 * 125 * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応) 126 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応 127 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加。 128 * 129 * @param isLineCnt 行数カウントの使用有無 130 * @param isMD5 ファイルのMD5の使用有無 131 */ 132 public FileLineModel( final boolean isLineCnt,final boolean isMD5 ) { 133 this( isLineCnt,isMD5,false,false ); // 6.3.1.0 (2015/06/28) 134 } 135 136 /** 137 * ラインカウントの有無と、MD5計算の有無と、コメント除外の可否を指定した、コンストラクターです。 138 * 139 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する) 140 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 141 * 142 * @param isLineCnt 行数カウントの使用有無 143 * @param isMD5 ファイルのMD5の使用有無 144 * @param isOmit コメント除外の可否(true:除外する) 145 */ 146 public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit ) { 147 this( isLineCnt,isMD5,isOmit,false ); // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 148 } 149 150 /** 151 * ラインカウントの有無と、MD5計算の有無と、コメント除外の可否と、追加属性可否を指定した、コンストラクターです。 152 * 153 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する) 154 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 155 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor 156 * 157 * @param isLineCnt 行数カウントの使用有無 158 * @param isMD5 ファイルのMD5の使用有無 159 * @param isOmit コメント除外の可否(true:除外する) 160 * @param isPath FILEPATH,ADDRESS,FILENAME 属性の可否(true:使用する) 161 */ 162 public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit,final boolean isPath ) { 163 super(); 164 // 4.3.4.4 (2009/01/01) 165 useLineCnt = isLineCnt; 166 useMD5 = isMD5; // 5.7.2.1 (2014/01/17) 167 omitCmnt = isOmit; // 5.7.4.0 (2014/03/07) 168 useFilePath = isPath; // 5.7.4.0 (2014/03/07) 169 init( KEYS ); 170 } 171 172 /** 173 * LineModel を元に、FileLineModel を構築します。 174 * これは、一旦ファイル等にセーブされた FileLineModel 形式を 175 * 元に戻す簡易コンストラクタです。 176 * 177 * @og.rev 4.2.3.0 (2008/05/26) 新規追加 178 * @og.rev 5.7.2.1 (2014/01/17) MD5の設定処理追加 179 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 180 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor 181 * 182 * @param model 元のLineModel 183 */ 184 public FileLineModel( final LineModel model ) { 185 super(); 186 // 4.3.4.4 (2009/01/01) 187 init( model.getNames() ); 188 189 final Object[] obj = model.getValues(); 190 191 setValue( LEVEL ,Integer.valueOf( (String)obj[LEVEL] ) ); 192 setValue( FILE ,new File((String)obj[FILE]) ); 193 setValue( LENGTH ,Long.valueOf( (String)obj[LENGTH] ) ); 194 setValue( MODIFY ,(String)obj[MODIFY] ); 195 196 final String cnt = (String)obj[LINECNT] ; 197 useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ; 198 if( useLineCnt ) { setValue( LINECNT ,cnt ); } 199 200 setValue( BIKO ,(String)obj[BIKO] ); 201 202 // 5.7.2.1 (2014/01/17) 203 final String md5Data = (String)obj[MD5] ; 204 useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ; 205 if( useMD5 ) { setValue( MD5 ,md5Data ); } 206 207 omitCmnt = false; // 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので、強制設定します。 208 209 // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 210 // 念のため、配列数をチェックしながら処理します。 211 if( obj.length > FILEPATH ) { 212 final String path = (String)obj[FILEPATH] ; 213 useFilePath = path != null && path.length() > 0 && ! "null".equalsIgnoreCase( path ) ; 214 if( useFilePath ) { 215 setValue( FILEPATH ,path ); 216 if( obj.length > ADDRESS ) { setValue( ADDRESS ,(String)obj[ADDRESS] ); } 217 if( obj.length > FILENAME ) { setValue( FILENAME ,(String)obj[FILENAME] ); } 218 } 219 } 220 else { 221 useFilePath = false; 222 } 223 } 224 225 /** 226 * File属性値をセットします。 227 * LEVEL,FILE,LENGTH,MODIFY,LINECNT,MD5 の各属性を設定します。 228 * 229 * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無 230 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 231 * @og.rev 5.7.2.1 (2014/01/17) MD5計算処理の追加 232 * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応 233 * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理 234 * @og.rev 6.2.1.0 (2015/03/13) ファイルの削除に失敗するため、削除しない。 235 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 236 * 237 * @param level ファイルのディレクトリ階層 238 * @param file ファイルオブジェクト 239 */ 240 public void setFileVals( final int level, final File file ) { 241 setValue( LEVEL ,Integer.valueOf( level ) ); 242 setValue( FILE ,file ); 243 setValue( MODIFY ,DateSet.getDate( file.lastModified(),modifyForm ) ); // 6.3.1.1 (2015/07/10) 244 245 // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理 246 // 別にコメント除去されたファイルを作成して、それの MD5 を求める。 247 File ocFile = null; 248 if( omitCmnt && useMD5 ) { 249 try { 250 ocFile = File.createTempFile( "temp",".tmp" ); 251 ocFile.deleteOnExit(); // 一応、このメソッド内で削除しますが、念のため。 252 } 253 catch( final IOException ex ) { 254 final String errMsg = "コメント除外のMD5計算用 temp ファイルの作成に失敗しました。" + ex.getMessage() ; 255 throw new OgRuntimeException( errMsg,ex ); 256 } 257 } 258 259 if( useLineCnt || omitCmnt ) { 260 final long[] cntVals = getLineCnt( file,ocFile ); // 5.7.7.1 (2014/06/13) 出力ファイルを渡します。 261 setValue( LINECNT ,String.valueOf( cntVals[0] ) ); 262 setValue( LENGTH ,Long.valueOf( cntVals[1] ) ); 263 } 264 else { 265 setValue( LENGTH ,Long.valueOf( file.length() ) ); 266 } 267 268 // 5.7.2.1 (2014/01/17) MD5計算がtrue で、かつ、ファイルの場合、MD5 計算を行います。 269 if( useMD5 && file.isFile() ) { 270 // 5.7.7.1 (2014/06/13) omitCmnt を考慮したMD5計算 271 if( ocFile == null ) { 272 setValue( MD5 ,HybsCryptography.getMD5( file ) ); 273 } 274 else { 275 setValue( MD5 ,HybsCryptography.getMD5( ocFile ) ); 276 // 6.0.2.4 (2014/10/17) RV java.io.File.delete() の例外的戻り値を無視しています。 277 // 6.2.1.0 (2015/03/13) ファイルの削除に失敗するため、削除しない。 278 } 279 } 280 281 // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加 282 if( useFilePath ) { 283 // FILEPATH は、正規のパス名文字列 を求めるが、エラー時は、絶対パス名文字列 にする。 284 try { 285 setValue( FILEPATH , file.getCanonicalPath() ); // 正規のパス名文字列 286 } 287 catch( final IOException ex ) { 288 setValue( FILEPATH , file.getAbsolutePath() ); // 絶対パス名文字列 289 } 290 291 // ADDRESS は、親の名前なので、直フォルダ名になる。 292 final File parent = file.getParentFile(); 293 if( parent != null ) { 294 setValue( ADDRESS ,parent.getName() ); 295 } 296 297 setValue( FILENAME ,file.getName() ); 298 } 299 } 300 301 /** 302 * コメント削除時の文字数計算で利用するファイルのエンコードをセットします。 303 * 初期値:JISAutoDetect 304 * 305 * @og.rev 5.7.4.0 (2014/03/07) 新規追加 306 * 307 * @param encode コメント削除時の文字数計算で利用するファイルのエンコード 308 */ 309 public void setEncode( final String encode ) { 310 this.encode = encode; 311 } 312 313 /** 314 * File属性値をセットします。 315 * 316 * @param file ファイルオブジェクト 317 */ 318 public void setFile( final File file ) { 319 setValue( FILE,file ); 320 } 321 322 /** 323 * ファイルを取得します。 324 * 325 * @return ファイル 326 */ 327 public File getFile() { 328 return (File)getValue( FILE ); 329 } 330 331 /** 332 * 備考情報属性値をセットします。 333 * 334 * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無 335 * 336 * @param biko 備考情報 337 */ 338 public void setBiko( final String biko ) { 339 setValue( BIKO,biko ); 340 } 341 342 /** 343 * レベル File属性値を取得します。 344 * 345 * @return ファイルのディレクトリ階層 346 */ 347 public int getLevel() { 348 return ((Integer)getValue( LEVEL )).intValue(); 349 } 350 351 /** 352 * ファイルサイズ File属性値を取得します。 353 * 354 * @return ファイルサイズ 355 */ 356 public long getLength() { 357 return ((Long)getValue( LENGTH )).longValue(); 358 } 359 360 /** 361 * 更新日時(Modify) のフォーマットを、指定します。 362 * 363 * ここで指定しない場合は、"yyyy/MM/dd HH:mm:ss" になります。 364 * Date型で変換できないようなフォームを指定した場合は、実行時に 365 * エラーになりますので、ご注意ください。 366 * 367 * @og.rev 6.3.1.1 (2015/07/10) Modify のフォーマットを、指定可能にします。 368 * 369 * @param form 更新日時のフォーマット 370 * @see java.text.SimpleDateFormat 371 */ 372 public void setModifyForm( final String form ) { 373 if( form != null && !form.isEmpty() ) { 374 modifyForm = form; 375 } 376 } 377 378 /** 379 * 更新日時 File属性値を取得します。 380 * 381 * @return 更新日時(yyyy/MM/dd HH:mm:ss) 382 */ 383 public String getModify() { 384 return (String)getValue( MODIFY ); 385 } 386 387 /** 388 * MD5 File属性値を取得します。 389 * ただし、useMD5 が true でないと値は返しません。 390 * 391 * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応) 392 * 393 * @return MD5の値 394 */ 395 public String getMD5() { 396 return (String)getValue( MD5 ); 397 } 398 399 /** 400 * 行数と文字数を取得します。 401 * 行数カウントとファイルの文字数カウント(バイト数ではありません)を行います。 402 * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。 403 * 一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、 404 * 先のLength(文字数)値とは異なりますのでご注意ください。 405 * 406 * 結果は、long型の配列で返します。[0]が行数で、[1]が文字数です。 407 * omitCmnt 属性を使用した場合は、コメント部分を削除した行数と文字数を求めます。 408 * これは、/* から */ の間、// から改行までです。 409 * ただし、"(二重引用符)で囲まれた文字列は、コメントとみなしません。 410 * 411 * @og.rev 5.7.4.0 (2014/03/07) 行数カウントとファイルの文字数カウントを行う。 412 * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理 413 * @og.rev 6.2.1.0 (2015/03/13) ディレクトリ以外からファイルのみに対象を変更。 414 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 415 * @og.rev 6.4.0.2 (2015/12/11) CommentLineParser 改造。 416 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 417 * 418 * @param file 行数を数えるファイルオブジェクト 419 * @param ocFile omitCmnt=trueの場合に、MD5計算する時の、仮出力ファイル(nullの場合は、無視) 420 * 421 * @return long型の配列([0]が行数で、[1]が文字数) 422 * @og.rtnNotNull 423 */ 424 private long[] getLineCnt( final File file,final File ocFile ) { 425 long lineCnt = 0L; // 行数 426 long charCnt = 0L; // 文字数 427 428 final BufferedReader reader = FileUtil.getBufferedReader( file,encode ); 429 430 // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理 431 PrintWriter writer = null; 432 if( ocFile != null ) { writer = FileUtil.getPrintWriter( ocFile ,encode ); } 433 434 // 6.4.0.2 (2015/12/11) CommentLineParser 改造 435 final CommentLineParser clp = omitCmnt ? new CommentLineParser( FileInfo.getSUFIX( file ) ) : null; 436 try { 437 // 6.2.1.0 (2015/03/13) ディレクトリ以外からファイルのみに対象を変更。 438 if( file.isFile() ) { 439 String line ; 440 while((line = reader.readLine()) != null) { 441 if( omitCmnt ) { 442 line = clp.line( line ); 443 if( line == null ) { continue; } // 戻り値が null の場合は、行として不成立 444 if( writer != null ) { writer.println( line ); } // 5.7.7.1 (2014/06/13) 445 } 446 447 lineCnt++; 448 charCnt += line.length(); 449 } 450 } 451 } 452 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 453 catch( final CharacterCodingException ex ) { 454 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 455 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 456 + " [" + file.getPath() + "] , Encode=[" + encode + "]" ; 457 throw new OgCharacterException( errMsg,ex ); // 6.5.0.1 (2016/10/21) 458 } 459 catch( final IOException ex ) { 460 final String errMsg = "ファイルカウント中に例外が発生しました。" + CR 461 + " [" + file.getPath() + "] , Encode=[" + encode + "]" ; 462 throw new OgRuntimeException( errMsg,ex ); 463 } 464 finally { 465 Closer.ioClose( reader ) ; 466 Closer.ioClose( writer ) ; // 5.7.7.1 (2014/06/13) ioClose は、引数が null なら無視します。 467 } 468 469 return new long[] { lineCnt,charCnt }; 470 } 471}