Coverage Report - com.orangesignal.jlha.LhaImmediateOutputStream
 
Classes in this File Line Coverage Branch Coverage Complexity
LhaImmediateOutputStream
0%
0/137
0%
0/66
3.619
LhaImmediateOutputStream$RandomAccessFileOutputStream
0%
0/18
0%
0/4
3.619
 
 1  
 /**
 2  
  * Copyright (C) 2002  Michel Ishizuka  All rights reserved.
 3  
  * 
 4  
  * 以下の条件に同意するならばソースとバイナリ形式の再配布と使用を
 5  
  * 変更の有無にかかわらず許可する。
 6  
  * 
 7  
  * 1.ソースコードの再配布において著作権表示と この条件のリスト
 8  
  *     および下記の声明文を保持しなくてはならない。
 9  
  * 
 10  
  * 2.バイナリ形式の再配布において著作権表示と この条件のリスト
 11  
  *     および下記の声明文を使用説明書もしくは その他の配布物内に
 12  
  *     含む資料に記述しなければならない。
 13  
  * 
 14  
  * このソフトウェアは石塚美珠瑠によって無保証で提供され、特定の目
 15  
  * 的を達成できるという保証、商品価値が有るという保証にとどまらず、
 16  
  * いかなる明示的および暗示的な保証もしない。
 17  
  * 石塚美珠瑠は このソフトウェアの使用による直接的、間接的、偶発
 18  
  * 的、特殊な、典型的な、あるいは必然的な損害(使用によるデータの
 19  
  * 損失、業務の中断や見込まれていた利益の遺失、代替製品もしくは
 20  
  * サービスの導入費等が考えられるが、決してそれだけに限定されない
 21  
  * 損害)に対して、いかなる事態の原因となったとしても、契約上の責
 22  
  * 任や無過失責任を含む いかなる責任があろうとも、たとえそれが不
 23  
  * 正行為のためであったとしても、またはそのような損害の可能性が報
 24  
  * 告されていたとしても一切の責任を負わないものとする。
 25  
  */
 26  
 
 27  
 package com.orangesignal.jlha;
 28  
 
 29  
 import java.io.File;
 30  
 import java.io.FileNotFoundException;
 31  
 import java.io.IOException;
 32  
 import java.io.OutputStream;
 33  
 import java.io.RandomAccessFile;
 34  
 import java.util.Properties;
 35  
 
 36  
 /**
 37  
  * 接続されたRandomAccessFileに 圧縮データを出力するためのユーティリティクラス。<br>
 38  
  * java.util.zip.ZipOutputStream と似たインターフェイスを持つように作った。<br>
 39  
  * 圧縮失敗時( 圧縮後サイズが圧縮前サイズを上回った場合 )の処理を 手動で行わなければならない。 以下に そのようなコードを示す。
 40  
  * 
 41  
  * <pre>
 42  
  * LhaCompressFiles( String arcfile, File[] files ){
 43  
  *   LhaImmediateOutputStream lio = new LhaImmediateOutputStream( arcfile );
 44  
  * 
 45  
  *   for( int i = 0 ; i &lt files.length ; i++ ){
 46  
  *     RandomAccessFile raf = new RandomAccessFile( files[i] );
 47  
  *     LhaHeader header = new LhaHeader( files[i].getName() );
 48  
  *     header.setLastModified( new Date( files.lastModified() ) );
 49  
  *     header.setOriginalSize( files.length() );
 50  
  *     byte[] buffer  = new byte[8192];
 51  
  *     int    length;
 52  
  * 
 53  
  *     while( 0 &lt= ( length = raf.read( buffer ) ) ){
 54  
  *         lio.write( buffer, 0, length );
 55  
  *     }
 56  
  * <strong>
 57  
  *     if( !lio.closeEntry() ){
 58  
  *       header.setCompressMethod( CompressMethod.LH0 );
 59  
  *       lio.putNextEntry( lhaheader );
 60  
  *       raf.seek( 0 );
 61  
  *       while( 0 &lt= ( length = raf.read( buffer ) ) ){
 62  
  *           lio.write( buffer, 0, length );
 63  
  *       }
 64  
  *       lio.closeEntry();
 65  
  *     }
 66  
  * </strong>
 67  
  *   lio.close();
 68  
  * }
 69  
  * </pre>
 70  
  * 
 71  
  * 進捗報告を実装する場合、このような処理をクラス内に隠蔽すると進捗報告は何秒間か 時によっては何十分も応答しなくなる。(例えばギガバイト級のデータを扱った場合) LhaRetainedOutputStream で発生する、このような事態を避けるために設計されている。<br>
 72  
  * また、JDK 1.1 以前では RandomAccessFile が setLength を持たないため、 書庫データの後ろに他のデータがある場合でもファイルサイズを切り詰めることが出来ない。<br>
 73  
  * この問題点は常にサイズ0の新しいファイルを開く事によって回避する事ができる。<br>
 74  
  * 
 75  
  * <pre>
 76  
  * -- revision history --
 77  
  * $Log: LhaImmediateOutputStream.java,v $
 78  
  * Revision 1.2  2002/12/11 02:25:06  dangan
 79  
  * [bug fix]
 80  
  *     jdk1.2 でコンパイルできなかった箇所を修正。
 81  
  * 
 82  
  * Revision 1.1  2002/12/08 00:00:00  dangan
 83  
  * [maintenance]
 84  
  *     LhaConstants から CompressMethod へのクラス名の変更に合わせて修正。
 85  
  * 
 86  
  * Revision 1.0  2002/08/05 00:00:00  dangan
 87  
  * add to version control
 88  
  * [change]
 89  
  *     コンストラクタから 引数に String encode を取るものを廃止、
 90  
  *     Properties を引数に取るものを追加。
 91  
  * [maintenance]
 92  
  *     ソース整備
 93  
  *     タブ廃止
 94  
  *     ライセンス文の修正
 95  
  * 
 96  
  * </pre>
 97  
  * 
 98  
  * @author $Author: dangan $
 99  
  * @version $Revision: 1.2 $
 100  
  */
 101  
 public class LhaImmediateOutputStream extends OutputStream {
 102  
 
 103  
         /**
 104  
          * 書庫ファイル
 105  
          */
 106  
         private RandomAccessFile archive;
 107  
 
 108  
         /**
 109  
          * 圧縮用出力ストリーム
 110  
          */
 111  
         private OutputStream out;
 112  
 
 113  
         /**
 114  
          * 現在圧縮中のヘッダ
 115  
          */
 116  
         private LhaHeader header;
 117  
 
 118  
         /**
 119  
          * ヘッダの出力に使用したエンコーディング
 120  
          */
 121  
         private String encoding;
 122  
 
 123  
         /**
 124  
          * ヘッダ位置
 125  
          */
 126  
         private long headerpos;
 127  
 
 128  
         /**
 129  
          * CRC値算出用
 130  
          */
 131  
         private CRC16 crc;
 132  
 
 133  
         /**
 134  
          * 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
 135  
          */
 136  
         private Properties property;
 137  
 
 138  
         // ------------------------------------------------------------------
 139  
         // Constructor
 140  
 
 141  
         /**
 142  
          * filename のファイルに 圧縮データを出力するOutputStreamを構築する。<br>
 143  
          * 各圧縮形式に対応した符号器の生成式等を持つプロパティには LhaProperty.getProperties() で得られたプロパティが使用される。<br>
 144  
          * 
 145  
          * @param filename 圧縮データを書きこむファイルの名前
 146  
          * @exception FileNotFoundException filename で与えられたファイルが見つからない場合。
 147  
          * @exception SecurityException セキュリティマネージャがファイルへのアクセスを許さない場合。
 148  
          * @see LhaProperty#getProperties()
 149  
          */
 150  0
         public LhaImmediateOutputStream(final String filename) throws FileNotFoundException {
 151  0
                 if (filename != null) {
 152  0
                         final RandomAccessFile file = new RandomAccessFile(filename, "rw");     // throws FileNotFoundException, SecurityException
 153  0
                         final Properties property = LhaProperty.getProperties();
 154  0
                         constructerHelper(file, property);
 155  0
                 } else {
 156  0
                         throw new NullPointerException("filename");
 157  
                 }
 158  0
         }
 159  
 
 160  
         /**
 161  
          * filename のファイルに 圧縮データを出力するOutputStreamを構築する。<br>
 162  
          * 
 163  
          * @param filename 圧縮データを書きこむファイルの名前
 164  
          * @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
 165  
          * @exception FileNotFoundException filename で与えられたファイルが見つからない場合。
 166  
          * @exception SecurityException セキュリティマネージャがファイルへのアクセスを許さない場合。
 167  
          * @see LhaProperty
 168  
          */
 169  0
         public LhaImmediateOutputStream(final String filename, final Properties property) throws FileNotFoundException {
 170  0
                 if (filename != null) {
 171  0
                         final RandomAccessFile file = new RandomAccessFile(filename, "rw");     // throws FileNotFoundException, SecurityException
 172  0
                         constructerHelper(file, property);
 173  0
                 } else {
 174  0
                         throw new NullPointerException("filename");
 175  
                 }
 176  0
         }
 177  
 
 178  
         /**
 179  
          * filename のファイルに 圧縮データを出力するOutputStreamを構築する。<br>
 180  
          * 各圧縮形式に対応した符号器の生成式等を持つプロパティには LhaProperty.getProperties() で得られたプロパティが使用される。<br>
 181  
          * 
 182  
          * @param filename 圧縮データを書きこむファイルの名前
 183  
          * @exception FileNotFoundException filename で与えられたファイルが見つからない場合。
 184  
          * @exception SecurityException セキュリティマネージャがファイルへのアクセスを許さない場合。
 185  
          * @exception IOException JDK1.2 でコンパイルするためだけに存在する。
 186  
          * @see LhaProperty#getProperties()
 187  
          */
 188  0
         public LhaImmediateOutputStream(final File filename) throws IOException {
 189  0
                 if (filename != null) {
 190  0
                         final RandomAccessFile file = new RandomAccessFile(filename, "rw");     // throws FileNotFoundException, SecurityException, IOException(jdk1.2)
 191  0
                         final Properties property = LhaProperty.getProperties();
 192  0
                         constructerHelper(file, property);
 193  0
                 } else {
 194  0
                         throw new NullPointerException("filename");
 195  
                 }
 196  0
         }
 197  
 
 198  
         /**
 199  
          * filename のファイルに 圧縮データを出力するOutputStreamを構築する。<br>
 200  
          * 
 201  
          * @param filename 圧縮データを書きこむファイルの名前
 202  
          * @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
 203  
          * @exception FileNotFoundException filename で与えられたファイルが見つからない場合。
 204  
          * @exception SecurityException セキュリティマネージャがファイルへのアクセスを許さない場合。
 205  
          * @exception IOException JDK1.2 でコンパイルするためだけに存在する。
 206  
          * @see LhaProperty
 207  
          */
 208  0
         public LhaImmediateOutputStream(final File filename, final Properties property) throws IOException {
 209  0
                 if (filename != null) {
 210  0
                         final RandomAccessFile file = new RandomAccessFile(filename, "rw");     // throws FileNotFoundException, SecurityException, IOException(jdk1.2)
 211  0
                         constructerHelper(file, property);
 212  0
                 } else {
 213  0
                         throw new NullPointerException("filename");
 214  
                 }
 215  0
         }
 216  
 
 217  
         /**
 218  
          * fileに 圧縮データを出力するOutputStreamを構築する。<br>
 219  
          * 各圧縮形式に対応した符号器の生成式等を持つプロパティには LhaProperty.getProperties() で得られたプロパティが使用される。<br>
 220  
          * 
 221  
          * @param file RandomAccessFile のインスタンス。<br>
 222  
          * <ul>
 223  
          * <li>既に close() されていない事。
 224  
          * <li>コンストラクタの mode には "rw" オプションを使用して、 読みこみと書きこみが出来るように生成されたインスタンスであること。
 225  
          * </ul>
 226  
          * の条件を満たすもの。
 227  
          * 
 228  
          * @see LhaProperty#getProperties()
 229  
          */
 230  0
         public LhaImmediateOutputStream(final RandomAccessFile file) {
 231  0
                 if (file != null) {
 232  0
                         final Properties property = LhaProperty.getProperties();
 233  0
                         constructerHelper(file, property);
 234  0
                 } else {
 235  0
                         throw new NullPointerException("out");
 236  
                 }
 237  0
         }
 238  
 
 239  
         /**
 240  
          * fileに 圧縮データを出力するOutputStreamを構築する。<br>
 241  
          * 各圧縮形式に対応した符号器の生成式等を持つプロパティには LhaProperty.getProperties() で得られたプロパティが使用される。<br>
 242  
          * 
 243  
          * @param file RandomAccessFile のインスタンス。<br>
 244  
          * <ul>
 245  
          * <li>既に close() されていない事。
 246  
          * <li>コンストラクタの mode には "rw" オプションを使用して、 読みこみと書きこみが出来るように生成されたインスタンスであること。
 247  
          * </ul>
 248  
          * の条件を満たすもの。
 249  
          * @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
 250  
          * 
 251  
          * @see LhaProperty
 252  
          */
 253  0
         public LhaImmediateOutputStream(final RandomAccessFile file, final Properties property) {
 254  0
                 if (file != null && property != null) {
 255  0
                         constructerHelper(file, property);                           // throws UnsupportedEncodingException
 256  0
                 } else if (file == null) {
 257  0
                         throw new NullPointerException("null");
 258  
                 } else {
 259  0
                         throw new NullPointerException("property");
 260  
                 }
 261  0
         }
 262  
 
 263  
         /**
 264  
          * コンストラクタの初期化処理を担当するメソッド。
 265  
          * 
 266  
          * @param file RandomAccessFile のインスタンス。<br>
 267  
          * <ul>
 268  
          * <li>既に close() されていない事。
 269  
          * <li>コンストラクタの mode には "rw" オプションを使用して、 読みこみと書きこみが出来るように生成されたインスタンスであること。
 270  
          * </ul>
 271  
          * の条件を満たすもの。
 272  
          * @param property 各圧縮形式に対応した符号器の生成式等が含まれるプロパティ
 273  
          */
 274  
         private void constructerHelper(final RandomAccessFile file, final Properties property) {
 275  0
                 archive = file;
 276  0
                 out = null;
 277  0
                 header = null;
 278  0
                 headerpos = -1;
 279  0
                 crc = new CRC16();
 280  0
                 this.property = property;
 281  0
         }
 282  
 
 283  
         // ------------------------------------------------------------------
 284  
         // method of java.io.OutputStream
 285  
 
 286  
         /**
 287  
          * 現在のエントリに1バイトのデータを書きこむ。
 288  
          * 
 289  
          * @param data 書きこむデータ
 290  
          * @exception IOException 入出力エラーが発生した場合。
 291  
          */
 292  
         @Override
 293  
         public void write(final int data) throws IOException {
 294  0
                 if (out != null) {
 295  0
                         if (header != null) {
 296  0
                                 crc.update(data);
 297  
                         }
 298  0
                         out.write(data);
 299  
                 } else {
 300  0
                         throw new IOException("no entry");
 301  
                 }
 302  0
         }
 303  
 
 304  
         /**
 305  
          * 現在のエントリに bufferの内容を全て書き出す。
 306  
          * 
 307  
          * @param buffer 書き出すデータの入ったバイト配列
 308  
          * @exception IOException 入出力エラーが発生した場合。
 309  
          */
 310  
         @Override
 311  
         public void write(final byte[] buffer) throws IOException {
 312  0
                 this.write(buffer, 0, buffer.length);
 313  0
         }
 314  
 
 315  
         /**
 316  
          * 現在のエントリに bufferの indexから lengthバイトのデータを書き出す。
 317  
          * 
 318  
          * @param buffer 書き出すデータの入ったバイト配列
 319  
          * @param index buffer内の書き出すべきデータの開始位置
 320  
          * @param length データのバイト数
 321  
          * @exception IOException 入出力エラーが発生した場合。
 322  
          */
 323  
         @Override
 324  
         public void write(final byte[] buffer, final int index, final int length) throws IOException {
 325  0
                 if (out != null) {
 326  0
                         if (header != null) {
 327  0
                                 crc.update(buffer, index, length);
 328  
                         }
 329  0
                         out.write(buffer, index, length);
 330  
                 } else {
 331  0
                         throw new IOException("no entry");
 332  
                 }
 333  0
         }
 334  
 
 335  
         /**
 336  
          * 現在書き込み中のエントリのデータを強制的に出力先に書き出す。 これは PostLzssEncoder, LzssOutputStream の規約どおり flush() しなかった場合とは別のデータを出力する。 (大抵の場合は 単に圧縮率が低下するだけである。)
 337  
          * 
 338  
          * @exception IOException 入出力エラーが発生した場合
 339  
          * @see PostLzssEncoder#flush()
 340  
          * @see LzssOutputStream#flush()
 341  
          */
 342  
         @Override
 343  
         public void flush() throws IOException {
 344  0
                 if (out != null) {
 345  0
                         out.flush();                                                   // throws IOException
 346  
                 } else {
 347  0
                         throw new IOException("no entry");
 348  
                 }
 349  0
         }
 350  
 
 351  
         /**
 352  
          * 出力先に全てのデータを出力し、ストリームを閉じる。<br>
 353  
          * また、使用していた全てのリソースを解放する。
 354  
          * 
 355  
          * @exception IOException 入出力エラーが発生した場合
 356  
          */
 357  
         @Override
 358  
         public void close() throws IOException {
 359  0
                 if (out != null) {
 360  0
                         closeEntry();                                                  // throws IOException
 361  
                 }
 362  
 
 363  
                 // ターミネータを出力
 364  0
                 archive.write(0);                                                // throws IOException
 365  
                 try {
 366  0
                         archive.setLength(archive.getFilePointer());            // After Java1.2 throws IOException
 367  0
                 } catch (final NoSuchMethodError error) {
 368  0
                 }
 369  0
                 archive.close();                                                   // throws IOException
 370  0
                 archive = null;
 371  
 
 372  0
                 crc = null;
 373  0
                 property = null;
 374  0
                 encoding = null;
 375  0
         }
 376  
 
 377  
         // ------------------------------------------------------------------
 378  
         // original method ( on the model of java.util.zip.ZipOutputStream )
 379  
 
 380  
         /**
 381  
          * 新しいエントリを書き込むようにストリームを設定する。<br>
 382  
          * このメソッドは 既に圧縮済みのエントリの場合は putNextEntryAlreadyCompressed(), 未だに圧縮されていない場合は putNextEntryNotYetCompressed() を呼び出す。<br>
 383  
          * 圧縮されているかの判定は、
 384  
          * <ul>
 385  
          * <li>header.getCompressedSize()<br>
 386  
          * <li>header.getCRC()<br>
 387  
          * </ul>
 388  
          * のどれか一つでも LhaHeader.UNKNOWN であれば未だに圧縮されていないとする。<br>
 389  
          * header には正確な OriginalSize が指定されている必要がある。<br>
 390  
          * 
 391  
          * @param header 書きこむエントリについての情報を持つ LhaHeaderのインスタンス。
 392  
          * 
 393  
          * @exception IOException 入出力エラーが発生した場合
 394  
          * @exception IllegalArgumentException header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
 395  
          */
 396  
         public void putNextEntry(final LhaHeader header) throws IOException {
 397  0
                 if (header.getCompressedSize() == LhaHeader.UNKNOWN
 398  
                                 || header.getCrc() == LhaHeader.UNKNOWN) {
 399  0
                         putNextEntryNotYetCompressed(header);                        // throws IOException
 400  
                 } else {
 401  0
                         putNextEntryAlreadyCompressed(header);                       // throws IOException
 402  
                 }
 403  0
         }
 404  
 
 405  
         /**
 406  
          * 既に圧縮済みのエントリを書きこむようにストリームを設定する。<br>
 407  
          * 圧縮済みデータが正しい事は、呼び出し側が保証する事。
 408  
          * 
 409  
          * @param header 書きこむエントリについての情報を持つ LhaHeaderのインスタンス。
 410  
          * 
 411  
          * @exception IOException 入出力エラーが発生した場合
 412  
          * @exception IllegalArgumentException <ol>
 413  
          * <li>header.getOriginalSize() が LhaHeader.UNKNOWN を返す場合
 414  
          * <li>header.getComressedSize() が LhaHeader.UNKNOWN を返す場合
 415  
          * <li>header.getCRC() が LhaHeader.UNKNOWN を返す場合
 416  
          * </ol>
 417  
          * の何れか。
 418  
          * @exception IllegalStateException 以前のエントリが未だに closeEntry() されていない場合
 419  
          */
 420  
         public void putNextEntryAlreadyCompressed(final LhaHeader header) throws IOException {
 421  0
                 if (out == null) {
 422  
 
 423  0
                         if (header.getOriginalSize() != LhaHeader.UNKNOWN
 424  
                                         && header.getCompressedSize() != LhaHeader.UNKNOWN
 425  
                                         && header.getCrc() != LhaHeader.UNKNOWN) {
 426  
 
 427  0
                                 headerpos = archive.getFilePointer();
 428  
 
 429  0
                                 encoding = property.getProperty("lha.encoding");
 430  0
                                 if (encoding == null) {
 431  0
                                         encoding = LhaProperty.getProperty("lha.encoding");
 432  
                                 }
 433  
 
 434  0
                                 archive.write(header.getBytes(encoding));                  // throws IOException
 435  0
                                 out = new RandomAccessFileOutputStream(archive,header.getCompressedSize());
 436  
 
 437  0
                         } else if (header.getOriginalSize() == LhaHeader.UNKNOWN) {
 438  0
                                 throw new IllegalArgumentException("OriginalSize must not \"LhaHeader.UNKNOWN\".");
 439  0
                         } else if (header.getCompressedSize() == LhaHeader.UNKNOWN) {
 440  0
                                 throw new IllegalArgumentException("CompressedSize must not \"LhaHeader.UNKNOWN\".");
 441  
                         } else {
 442  0
                                 throw new IllegalArgumentException("CRC must not \"LhaHeader.UNKNOWN\".");
 443  
                         }
 444  
                 } else {
 445  0
                         throw new IllegalStateException("entry is not closed.");
 446  
                 }
 447  0
         }
 448  
 
 449  
         /**
 450  
          * 未だに圧縮されていないエントリを書きこむようにストリームを 設定する。header に CompressedSize,CRCが指定されていても無 視される。このメソッドに渡される header には LhaHeader.setOriginalSize() を用いて 正確なオリジナルサイズ が指定されている必要がある。
 451  
          * 
 452  
          * @param header 書きこむエントリについての情報を持つ LhaHeaderのインスタンス。
 453  
          * @exception IOException 入出力エラーが発生した場合
 454  
          * @exception IllegalArgumentException header.getOriginalSize() が LhaHeader.UNKNOWN を返した場合
 455  
          * @exception IllegalStateException 以前のエントリが未だに closeEntry() されていない場合
 456  
          */
 457  
         public void putNextEntryNotYetCompressed(final LhaHeader header) throws IOException {
 458  0
                 if (out == null) {
 459  0
                         if (header.getOriginalSize() != LhaHeader.UNKNOWN) {
 460  
 
 461  0
                                 crc.reset();
 462  0
                                 headerpos = archive.getFilePointer();
 463  0
                                 this.header = (LhaHeader) header.clone();
 464  0
                                 this.header.setCompressedSize(0);
 465  0
                                 this.header.setCrc(0);
 466  
 
 467  0
                                 encoding = property.getProperty("lha.encoding");
 468  0
                                 if (encoding == null) {
 469  0
                                         encoding = LhaProperty.getProperty("lha.encoding");
 470  
                                 }
 471  
 
 472  0
                                 archive.write(this.header.getBytes(encoding));
 473  0
                                 out = new RandomAccessFileOutputStream(archive,
 474  
                                                 header.getOriginalSize());
 475  0
                                 out = CompressMethod.connectEncoder(out,
 476  
                                                 header.getCompressMethod(), property);
 477  
 
 478  
                         } else {
 479  0
                                 throw new IllegalArgumentException("OriginalSize must not \"LhaHeader.UNKNOWN\".");
 480  
                         }
 481  
                 } else {
 482  0
                         throw new IllegalStateException("entry is not closed.");
 483  
                 }
 484  0
         }
 485  
 
 486  
         /**
 487  
          * 現在出力中のエントリを閉じ、次のエントリが出力可能な状態にする。<br>
 488  
          * putNextEntryNotYetCompressed() で開いたエントリを閉じる場合 このメソッドは圧縮に失敗した(圧縮後サイズが圧縮前サイズを上回った)場合、 エントリ全体を書き込み先 の RandomAccessFile から削除する。<br>
 489  
          * この削除処理は単に ファイルポインタを エントリ開始位置まで巻き戻すだけなので RandomAccessFile に setLength() が無い jdk1.1 以前では エントリを無圧縮(もしくは他の圧縮法)で再出力しない場合、 書庫データの終端以降に圧縮に失敗した不完全なデータが残ったままになる。<br>
 490  
          * 
 491  
          * @return エントリが出力された場合は true、 圧縮前よりも圧縮後の方がサイズが大きくなったため、 エントリが削除された場合は false。 また、現在処理中のエントリが無かった場合も true を返す。
 492  
          * 
 493  
          * @exception IOException 入出力エラーが発生した場合
 494  
          */
 495  
         public boolean closeEntry() throws IOException {
 496  0
                 if (out != null) {
 497  
 
 498  0
                         out.close();
 499  0
                         if (header != null) {
 500  
 
 501  0
                                 final long pos = archive.getFilePointer();
 502  0
                                 final long size = pos - headerpos
 503  
                                                 - header.getBytes(encoding).length;
 504  
 
 505  0
                                 header.setCompressedSize(size);
 506  0
                                 if (header.getCrc() != LhaHeader.NO_CRC) {
 507  0
                                         header.setCrc((int) crc.getValue());
 508  
                                 }
 509  
 
 510  0
                                 archive.seek(headerpos);
 511  0
                                 if (header.getCompressMethod().equals(CompressMethod.LH0)
 512  
                                                 || header.getCompressMethod()
 513  
                                                                 .equals(CompressMethod.LHD)
 514  
                                                 || header.getCompressMethod()
 515  
                                                                 .equals(CompressMethod.LZ4)
 516  
                                                 || header.getCompressedSize() < header
 517  
                                                                 .getOriginalSize()) {
 518  
 
 519  0
                                         archive.write(header.getBytes(encoding));
 520  0
                                         archive.seek(pos);
 521  0
                                         header = null;
 522  0
                                         out = null;
 523  0
                                         return true;
 524  
                                 }
 525  0
                                 header = null;
 526  0
                                 out = null;
 527  0
                                 return false;
 528  
                         }
 529  0
                         out = null;
 530  0
                         return true;
 531  
                 }
 532  0
                 return true;
 533  
         }
 534  
 
 535  
         // ------------------------------------------------------------------
 536  
         // inner classes
 537  
         // ------------------------------------------------------------------
 538  
         // private static class RandomAccessFileOutputStream
 539  
         // ------------------------------------------------------------------
 540  
         /**
 541  
          * RandomAccessFileをOutputStreamのインタフェイスに合わせるためのラッパクラス
 542  
          */
 543  
         private static class RandomAccessFileOutputStream extends OutputStream {
 544  
 
 545  
                 /**
 546  
                  * 出力先RandomAccessFile
 547  
                  */
 548  
                 private RandomAccessFile archive;
 549  
 
 550  
                 /**
 551  
                  * 現在処理位置
 552  
                  */
 553  
                 private long pos;
 554  
 
 555  
                 /**
 556  
                  * 格納限界
 557  
                  */
 558  
                 private final long limit;
 559  
 
 560  
                 // ------------------------------------------------------------------
 561  
                 // Consutructor
 562  
 
 563  
                 /**
 564  
                  * RandomAccessFile をラップした OutputStream を構築する。
 565  
                  * 
 566  
                  * @param archive 出力先のRandomAccessFile
 567  
                  * @param length 出力限界長
 568  
                  * @exception IOException 入出力エラーエラーが発生した場合
 569  
                  */
 570  0
                 public RandomAccessFileOutputStream(final RandomAccessFile archive, final long length) throws IOException {
 571  0
                         this.archive = archive;
 572  0
                         pos = this.archive.getFilePointer();                       // throws IOException
 573  0
                         limit = pos + length;
 574  0
                 }
 575  
 
 576  
                 // ------------------------------------------------------------------
 577  
                 // method of java.io.OutputStream
 578  
 
 579  
                 /**
 580  
                  * 接続されたRandomAccessFileに1バイト書きこむ。<br>
 581  
                  * コンストラクタに渡された限界を超えて書き込もうとした場合は 何も行わない。
 582  
                  * 
 583  
                  * @param data 書きこむ1byteのデータ
 584  
                  * @exception IOException 入出力エラーが発生した場合
 585  
                  */
 586  
                 @Override
 587  
                 public void write(final int data) throws IOException {
 588  0
                         if (pos < limit) {
 589  0
                                 pos++;
 590  0
                                 archive.write(data);                                     // throws IOException
 591  
                         }
 592  0
                 }
 593  
 
 594  
                 /**
 595  
                  * 接続されたRandomAccessFileにbufferの内容を全て書きこむ。 コンストラクタに渡された限界を超えて書き込もうとした場合は 何も行わない。
 596  
                  * 
 597  
                  * @param buffer 書きこむデータの入ったバイト配列
 598  
                  * @exception IOException 入出力エラーが発生した場合
 599  
                  */
 600  
                 @Override
 601  
                 public void write(final byte[] buffer) throws IOException {
 602  0
                         this.write(buffer, 0, buffer.length);                             // throws IOException
 603  0
                 }
 604  
 
 605  
                 /**
 606  
                  * 接続されたRandomAccessFileにbufferの内容をindexから lengthバイト書きこむ。 コンストラクタに渡された限界を超えて書き込もうとした場合は 何も行わない。
 607  
                  * 
 608  
                  * @param buffer 書きこむデータの入ったバイト配列
 609  
                  * @param index buffer内の書きこむデータの開始位置
 610  
                  * @param length 書きこむデータ量
 611  
                  * @exception IOException 入出力エラーが発生した場合
 612  
                  */
 613  
                 @Override
 614  
                 public void write(final byte[] buffer, final int index, int length) throws IOException {
 615  0
                         if (limit < pos + length) {
 616  0
                                 length = (int) Math.max(limit - pos, 0);
 617  
                         }
 618  0
                         archive.write(buffer, index, length);                        // throws IOException
 619  0
                         pos += length;
 620  0
                 }
 621  
 
 622  
                 /**
 623  
                  * このストリームを閉じて 使用していたリソースを開放する。<br>
 624  
                  */
 625  
                 @Override
 626  
                 public void close() {
 627  0
                         archive = null;
 628  0
                 }
 629  
 
 630  
         }
 631  
 
 632  
 }