1// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.archivepatcher.shared;
16
17import java.io.File;
18import java.io.IOException;
19import java.io.OutputStream;
20import java.io.RandomAccessFile;
21
22/**
23 * An {@link OutputStream} backed by a file that will be written serially. Allows pre-allocating
24 * the space for a stream as a file and then writing to that file as a stream. Call {@link #flush()}
25 * to force the data to be written to the backing storage.
26 */
27public class RandomAccessFileOutputStream extends OutputStream {
28  /**
29   * The backing {@link RandomAccessFile}.
30   */
31  private final RandomAccessFile raf;
32
33  /**
34   * Constructs a new instance that will immediately open the specified file for writing and set
35   * the length to the specified value.
36   * @param outputFile the file to wrap
37   * @param expectedSize if greater than or equal to zero, the size to set the file to immediately;
38   * otherwise, the file size is not set
39   * @throws IOException if unable to open the file for writing or set the size
40   */
41  public RandomAccessFileOutputStream(File outputFile, long expectedSize) throws IOException {
42    this.raf = getRandomAccessFile(outputFile);
43    if (expectedSize >= 0) {
44      raf.setLength(expectedSize);
45      if (raf.length() != expectedSize) {
46        throw new IOException("Unable to set the file size");
47      }
48    }
49  }
50
51  /**
52   * Given a {@link File}, get a writeable {@link RandomAccessFile} reference for it.
53   * @param file the file
54   * @return as described
55   * @throws IOException if unable to open the file
56   */
57  protected RandomAccessFile getRandomAccessFile(File file) throws IOException {
58    return new RandomAccessFile(file, "rw");
59  }
60
61  @Override
62  public void write(int b) throws IOException {
63    raf.write(b);
64  }
65
66  @Override
67  public void write(byte[] b) throws IOException {
68    write(b, 0, b.length);
69  }
70
71  @Override
72  public void write(byte[] b, int off, int len) throws IOException {
73    raf.write(b, off, len);
74  }
75
76  @Override
77  public void flush() throws IOException {
78    raf.getChannel().force(true);
79  }
80
81  @Override
82  public void close() throws IOException {
83    flush();
84    raf.close();
85  }
86}
87