001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.vfs2;
018
019import java.io.Closeable;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023import java.nio.charset.Charset;
024import java.security.cert.Certificate;
025import java.util.Arrays;
026import java.util.Map;
027
028import org.apache.commons.vfs2.provider.AbstractFileObject;
029import org.apache.commons.vfs2.util.RandomAccessMode;
030
031/**
032 * Represents the data content of a file.
033 * <p>
034 * To read from a file, use the {@code InputStream} returned by {@link #getInputStream()}.
035 * </p>
036 * <p>
037 * To write to a file, use the {@code OutputStream} returned by {@link #getOutputStream()} method. This will create the
038 * file, and the parent folder, if necessary.
039 * </p>
040 * <p>
041 * A file may have multiple InputStreams open at the same time.
042 * </p>
043 *
044 * @see FileObject#getContent
045 */
046public interface FileContent extends Closeable {
047
048    /**
049     * Closes all resources used by the content, including any open stream. Commits pending changes to the file.
050     * <p>
051     * This method is a hint to the implementation that it can release resources. This object can continue to be used
052     * after calling this method.
053     * </p>
054     *
055     * @throws FileSystemException if an error occurs closing the file.
056     */
057    @Override
058    void close() throws FileSystemException;
059
060    /**
061     * Gets the value of an attribute of the file's content.
062     *
063     * @param attrName The name of the attribute. Attribute names are case-insensitive.
064     * @return The value of the attribute, or null if the attribute value is unknown.
065     * @throws FileSystemException If the file does not exist, or does not support attributes.
066     */
067    Object getAttribute(String attrName) throws FileSystemException;
068
069    /**
070     * Gets the attributes of the file's content.
071     *
072     * @return The names of the attributes. Never returns null;
073     * @throws FileSystemException If the file does not exist, or does not support attributes.
074     */
075    String[] getAttributeNames() throws FileSystemException;
076
077    /**
078     * Returns a read-only map of this file's attributes.
079     *
080     * @return The attribute Map.
081     * @throws FileSystemException If the file does not exist, or does not support attributes.
082     */
083    Map<String, Object> getAttributes() throws FileSystemException;
084
085    /**
086     * Returns the content of a file as a byte array.
087     *
088     * @return The content as a byte array.
089     * @throws IOException if the file content cannot be accessed.
090     * @since 2.4
091     */
092    default byte[] getByteArray() throws IOException {
093        final long sizeL = getSize();
094        if (sizeL > Integer.MAX_VALUE) {
095            throw new IllegalStateException(String.format("File content is too large for a byte array: %,d", sizeL));
096        }
097        final boolean sizeUndefined = sizeL < 0;
098        final int size = sizeUndefined ? AbstractFileObject.DEFAULT_BUFFER_SIZE : (int) sizeL;
099        final byte[] buf = new byte[size];
100        int pos;
101        try (InputStream in = getInputStream(size)) {
102            int read = 0;
103            for (pos = 0; pos < size && read >= 0; pos += read) {
104                read = in.read(buf, pos, size - pos);
105            }
106        }
107        return sizeUndefined && pos < buf.length ? Arrays.copyOf(buf, ++pos) : buf;
108    }
109
110    /**
111     * Gets the certificates if any used to sign this file or folder.
112     *
113     * @return The certificates, or an empty array if there are no certificates or the file does not support signing.
114     * @throws FileSystemException If the file does not exist, or is being written.
115     */
116    Certificate[] getCertificates() throws FileSystemException;
117
118    /**
119     * Gets the content info. e.g. type, encoding, ...
120     *
121     * @return the FileContentInfo
122     * @throws FileSystemException if an error occurs.
123     */
124    FileContentInfo getContentInfo() throws FileSystemException;
125
126    /**
127     * Gets the file which this is the content of.
128     *
129     * @return The FileObject this is the content of.
130     */
131    FileObject getFile();
132
133    /**
134     * Gets an input stream for reading the file's content.
135     * <p>
136     * There may only be a single input or output stream open for the file at any time.
137     * </p>
138     *
139     * @return An input stream to read the file's content from. The input stream is buffered, so there is no need to
140     *         wrap it in a {@code BufferedInputStream}.
141     * @throws FileSystemException If the file does not exist, or is being read, or is being written, or on error
142     *         opening the stream.
143     */
144    InputStream getInputStream() throws FileSystemException;
145
146    /**
147     * Gets an input stream for reading the file's content.
148     * <p>
149     * There may only be a single input or output stream open for the file at any time.
150     * </p>
151     *
152     * @param bufferSize The buffer size to use.
153     * @return An input stream to read the file's content from. The input stream is buffered, so there is no need to
154     *         wrap it in a {@code BufferedInputStream}.
155     * @throws FileSystemException If the file does not exist, or is being read, or is being written, or on error
156     *         opening the stream.
157     * @since 2.4
158     */
159    default InputStream getInputStream(final int bufferSize) throws FileSystemException {
160        return getInputStream();
161    }
162
163    /**
164     * Gets the last-modified timestamp of the file.
165     *
166     * @return The last-modified timestamp.
167     * @throws FileSystemException If the file does not exist, or is being written to, or on error determining the
168     *         last-modified timestamp.
169     */
170    long getLastModifiedTime() throws FileSystemException;
171
172    /**
173     * Gets an output stream for writing the file's content.
174     * <p>
175     * If the file does not exist, this method creates it, and the parent folder, if necessary. If the file does exist,
176     * it is replaced with whatever is written to the output stream.
177     * </p>
178     * <p>
179     * There may only be a single input or output stream open for the file at any time.
180     * </p>
181     *
182     * @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
183     *         in a {@code BufferedOutputStream}.
184     * @throws FileSystemException If the file is read-only, or is being read, or is being written, or on error opening
185     *         the stream.
186     */
187    OutputStream getOutputStream() throws FileSystemException;
188
189    /**
190     * Gets an output stream for writing the file's content.
191     * <p>
192     * If the file does not exist, this method creates it, and the parent folder, if necessary. If the file does exist,
193     * it is replaced with whatever is written to the output stream.
194     * </p>
195     * <p>
196     * There may only be a single input or output stream open for the file at any time.
197     * </p>
198     *
199     * @param bAppend true if you would like to append to the file. This may not be supported by all implementations.
200     * @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
201     *         in a {@code BufferedOutputStream}.
202     * @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
203     *         and the implementation does not support it, or on error opening the stream.
204     */
205    OutputStream getOutputStream(boolean bAppend) throws FileSystemException;
206
207    /**
208     * Gets an output stream for writing the file's content.
209     * <p>
210     * If the file does not exist, this method creates it, and the parent folder, if necessary. If the file does exist,
211     * it is replaced with whatever is written to the output stream.
212     * </p>
213     * <p>
214     * There may only be a single input or output stream open for the file at any time.
215     * </p>
216     *
217     * @param bAppend true if you would like to append to the file. This may not be supported by all implementations.
218     * @param bufferSize The buffer size to use.
219     * @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
220     *         in a {@code BufferedOutputStream}.
221     * @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
222     *         and the implementation does not support it, or on error opening the stream.
223     * @since 2.4
224     */
225    default OutputStream getOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
226        return getOutputStream(bAppend);
227    }
228
229    /**
230     * Gets an output stream for writing the file's content.
231     * <p>
232     * If the file does not exist, this method creates it, and the parent folder, if necessary. If the file does exist,
233     * it is replaced with whatever is written to the output stream.
234     * </p>
235     * <p>
236     * There may only be a single input or output stream open for the file at any time.
237     * </p>
238     *
239     * @param bufferSize The buffer size to use.
240     * @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
241     *         in a {@code BufferedOutputStream}.
242     * @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
243     *         and the implementation does not support it, or on error opening the stream.
244     * @since 2.4
245     */
246    default OutputStream getOutputStream(final int bufferSize) throws FileSystemException {
247        return getOutputStream();
248    }
249
250    /**
251     * Gets a stream for reading/writing the file's content.
252     * <p>
253     * If the file does not exist, and you use one of the write* methods, this method creates it, and the parent folder,
254     * if necessary. If the file does exist, parts of the file are replaced with whatever is written at a given
255     * position.
256     * </p>
257     * <p>
258     * There may only be a single input or output stream open for the file at any time.
259     * </p>
260     *
261     * @param mode The mode to use to access the file.
262     * @return the stream for reading and writing the file's content.
263     * @throws FileSystemException If the file is read-only, or is being read, or is being written, or on error opening
264     *         the stream.
265     */
266    RandomAccessContent getRandomAccessContent(RandomAccessMode mode) throws FileSystemException;
267
268    /**
269     * Gets the size of the file, in bytes.
270     *
271     * @return The size of the file, in bytes.
272     * @throws FileSystemException If the file does not exist, or is being written to, or on error determining the size.
273     */
274    long getSize() throws FileSystemException;
275
276    /**
277     * Returns the content of a file as a String.
278     *
279     * @param charset The file character set, may be null.
280     * @return The content as a byte array.
281     * @throws IOException if the file content cannot be accessed.
282     * @since 2.4
283     */
284    default String getString(final Charset charset) throws IOException {
285        return new String(getByteArray(), charset == null ? Charset.defaultCharset() : charset);
286    }
287
288    /**
289     * Gets the content of a file as a String.
290     *
291     * @param charset The file character set, may be null.
292     * @return The content as a byte array.
293     * @throws IOException if the file content cannot be accessed.
294     * @since 2.4
295     */
296    default String getString(final String charset) throws IOException {
297        return new String(getByteArray(), charset == null ? Charset.defaultCharset().name() : charset);
298    }
299
300    /**
301     * Checks if an attribute of the file's content exists.
302     *
303     * @param attrName The name of the attribute.
304     * @return true if the attribute exists, false otherwise.
305     * @throws FileSystemException If the file does not exist, or does not support attributes.
306     */
307    boolean hasAttribute(String attrName) throws FileSystemException;
308
309    /**
310     * Tests if the receiver is empty.
311     *
312     * @return true if the receiver is empty, false otherwise.
313     * @throws FileSystemException If the file does not exist, or is being written to, or on error determining the size.
314     * @since 2.5.0
315     */
316    default boolean isEmpty() throws FileSystemException {
317        return getSize() <= 0;
318    }
319
320    /**
321     * Tests if this file has open streams.
322     *
323     * @return true if the file is open, false otherwise.
324     */
325    boolean isOpen();
326
327    /**
328     * Removes the value of an attribute of the file's content.
329     *
330     * @param attrName The name of the attribute.
331     * @throws FileSystemException If the file does not exist, or is read-only, or does not support attributes, or on
332     *         error removing the attribute.
333     */
334    void removeAttribute(String attrName) throws FileSystemException;
335
336    /**
337     * Sets the value of an attribute of the file's content. Creates the file if it does not exist.
338     *
339     * @param attrName The name of the attribute.
340     * @param value The value of the attribute.
341     * @throws FileSystemException If the file does not exist, or is read-only, or does not support attributes, or on
342     *         error setting the attribute.
343     */
344    void setAttribute(String attrName, Object value) throws FileSystemException;
345
346    /**
347     * Sets the last-modified timestamp of the file. Creates the file if it does not exist.
348     *
349     * @param modTime The time to set the last-modified timestamp to.
350     * @throws FileSystemException If the file is read-only, or is being written to, or on error setting the
351     *         last-modified timestamp.
352     */
353    void setLastModifiedTime(long modTime) throws FileSystemException;
354
355    /**
356     * Writes this content to another FileContent.
357     *
358     * @param output The target OutputStream.
359     * @throws IOException if an error occurs writing the content.
360     * @return the total number of bytes written
361     * @since 2.1
362     */
363    long write(FileContent output) throws IOException;
364
365    /**
366     * Writes this content to another FileObject.
367     *
368     * @param file The target FileObject.
369     * @throws IOException if an error occurs writing the content.
370     * @return the total number of bytes written
371     * @since 2.1
372     */
373    long write(FileObject file) throws IOException;
374
375    /**
376     * Writes this content to an OutputStream.
377     *
378     * @param output The target OutputStream.
379     * @return the total number of bytes written
380     * @throws IOException if an error occurs writing the content.
381     * @since 2.1
382     */
383    long write(OutputStream output) throws IOException;
384
385    /**
386     * Writes this content to an OutputStream.
387     *
388     * @param output The target OutputStream.
389     * @param bufferSize The buffer size to write data chunks.
390     * @return the total number of bytes written
391     * @throws IOException if an error occurs writing the file.
392     * @since 2.1
393     */
394    long write(OutputStream output, int bufferSize) throws IOException;
395
396}