View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.archivers;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.nio.file.LinkOption;
25  import java.nio.file.Path;
26  
27  /**
28   * Archive output stream implementations are expected to override the {@link #write(byte[], int, int)} method to improve performance. They should also override
29   * {@link #close()} to ensure that any necessary trailers are added.
30   *
31   * <p>
32   * The normal sequence of calls when working with ArchiveOutputStreams is:
33   * </p>
34   * <ul>
35   * <li>Create ArchiveOutputStream object,</li>
36   * <li>optionally write SFX header (Zip only),</li>
37   * <li>repeat as needed:
38   * <ul>
39   * <li>{@link #putArchiveEntry(ArchiveEntry)} (writes entry header),
40   * <li>{@link #write(byte[])} (writes entry data, as often as needed),
41   * <li>{@link #closeArchiveEntry()} (closes entry),
42   * </ul>
43   * </li>
44   * <li>{@link #finish()} (ends the addition of entries),</li>
45   * <li>optionally write additional data, provided format supports it,</li>
46   * <li>{@link #close()}.</li>
47   * </ul>
48   *
49   * @param <E> The type of {@link ArchiveEntry} consumed.
50   */
51  public abstract class ArchiveOutputStream<E extends ArchiveEntry> extends OutputStream {
52  
53      static final int BYTE_MASK = 0xFF;
54  
55      /** Temporary buffer used for the {@link #write(int)} method. */
56      private final byte[] oneByte = new byte[1];
57  
58      /** Holds the number of bytes written to this stream. */
59      private long bytesWritten;
60  
61      /**
62       * Whether this stream is able to write the given entry.
63       *
64       * <p>
65       * Some archive formats support variants or details that are not supported (yet).
66       * </p>
67       *
68       * @param archiveEntry the entry to test
69       * @return This implementation always returns true.
70       * @since 1.1
71       */
72      public boolean canWriteEntryData(final ArchiveEntry archiveEntry) {
73          return true;
74      }
75  
76      /**
77       * Closes the archive entry, writing any trailer information that may be required.
78       *
79       * @throws IOException if an I/O error occurs
80       */
81      public abstract void closeArchiveEntry() throws IOException;
82  
83      /**
84       * Increments the counter of already written bytes. Doesn't increment if EOF has been hit ({@code written == -1}).
85       *
86       * @param written the number of bytes written
87       */
88      protected void count(final int written) {
89          count((long) written);
90      }
91  
92      /**
93       * Increments the counter of already written bytes. Doesn't increment if EOF has been hit ({@code written == -1}).
94       *
95       * @param written the number of bytes written
96       * @since 1.1
97       */
98      protected void count(final long written) {
99          if (written != -1) {
100             bytesWritten += written;
101         }
102     }
103 
104     /**
105      * Creates an archive entry using the inputFile and entryName provided.
106      *
107      * @param inputFile the file to create the entry from
108      * @param entryName name to use for the entry
109      * @return the ArchiveEntry set up with details from the file
110      *
111      * @throws IOException if an I/O error occurs
112      */
113     public abstract E createArchiveEntry(File inputFile, String entryName) throws IOException;
114 
115     /**
116      * Creates an archive entry using the inputPath and entryName provided.
117      * <p>
118      * The default implementation calls simply delegates as:
119      * </p>
120      *
121      * <pre>
122      * return createArchiveEntry(inputFile.toFile(), entryName);
123      * </pre>
124      * <p>
125      * Subclasses should override this method.
126      * </p>
127      *
128      * @param inputPath the file to create the entry from
129      * @param entryName name to use for the entry
130      * @param options   options indicating how symbolic links are handled.
131      * @return the ArchiveEntry set up with details from the file
132      *
133      * @throws IOException if an I/O error occurs
134      * @since 1.21
135      */
136     public E createArchiveEntry(final Path inputPath, final String entryName, final LinkOption... options) throws IOException {
137         return createArchiveEntry(inputPath.toFile(), entryName);
138     }
139 
140     /**
141      * Finishes the addition of entries to this stream, without closing it. Additional data can be written, if the format supports it.
142      *
143      * @throws IOException if the user forgets to close the entry.
144      */
145     public abstract void finish() throws IOException;
146 
147     /**
148      * Gets the current number of bytes written to this stream.
149      *
150      * @return the number of written bytes
151      * @since 1.1
152      */
153     public long getBytesWritten() {
154         return bytesWritten;
155     }
156 
157     /**
158      * Gets the current number of bytes written to this stream.
159      *
160      * @return the number of written bytes
161      * @deprecated this method may yield wrong results for large archives, use #getBytesWritten instead
162      */
163     @Deprecated
164     public int getCount() {
165         return (int) bytesWritten;
166     }
167 
168     /**
169      * Writes the headers for an archive entry to the output stream. The caller must then write the content to the stream and call {@link #closeArchiveEntry()}
170      * to complete the process.
171      *
172      * @param entry describes the entry
173      * @throws IOException if an I/O error occurs
174      */
175     public abstract void putArchiveEntry(E entry) throws IOException;
176 
177     /**
178      * Writes a byte to the current archive entry.
179      *
180      * <p>
181      * This method simply calls {@code write( byte[], 0, 1 )}.
182      *
183      * <p>
184      * MUST be overridden if the {@link #write(byte[], int, int)} method is not overridden; may be overridden otherwise.
185      *
186      * @param b The byte to be written.
187      * @throws IOException on error
188      */
189     @Override
190     public void write(final int b) throws IOException {
191         oneByte[0] = (byte) (b & BYTE_MASK);
192         write(oneByte, 0, 1);
193     }
194 }