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 */
017
018package org.apache.commons.io.output;
019
020import java.io.File;
021import java.io.IOException;
022import java.io.OutputStream;
023import java.io.RandomAccessFile;
024import java.nio.file.StandardOpenOption;
025
026import org.apache.commons.io.build.AbstractOrigin;
027import org.apache.commons.io.build.AbstractStreamBuilder;
028
029/**
030 * An {@link OutputStream} that writes to a {@link RandomAccessFile}.
031 *
032 * @since 2.18.0
033 */
034public final class RandomAccessFileOutputStream extends OutputStream {
035
036    // @formatter:off
037    /**
038     * Builds a new {@link RandomAccessFileOutputStream}.
039     * <p>
040     * For example:
041     * </p>
042     * <pre>{@code
043     * RandomAccessFileOutputStream s = RandomAccessFileOutputStream.builder()
044     *   .setFile("myfile.txt")
045     *   .setOpenOptions(StandardOpenOption.SYNC)
046     *   .get();}
047     * </pre>
048     * <p>
049     * The only super's aspect used is buffer size.
050     * </p>
051     *
052     * @see #get()
053     */
054    // @formatter:on
055    public static final class Builder extends AbstractStreamBuilder<RandomAccessFileOutputStream, Builder> {
056
057        /**
058         * Use {@link RandomAccessFileOutputStream#builder()}.
059         */
060        private Builder() {
061            setOpenOptions(StandardOpenOption.WRITE);
062        }
063
064        /**
065         * Builds a new {@link RandomAccessFileOutputStream}.
066         * <p>
067         * You must set an aspect that supports {@link RandomAccessFile} or {@link File}, otherwise, this method throws an exception. Only set one of
068         * RandomAccessFile or an origin that can be converted to a File.
069         * </p>
070         * <p>
071         * This builder uses the following aspects:
072         * </p>
073         * <ul>
074         * <li>{@link RandomAccessFile} is the target aspect.</li>
075         * <li>{@link File}</li>
076         * <li>closeOnClose</li>
077         * </ul>
078         *
079         * @return a new instance.
080         * @throws IllegalStateException         if the {@code origin} is {@code null}.
081         * @throws IllegalStateException         if both RandomAccessFile and origin are set.
082         * @throws UnsupportedOperationException if the origin cannot be converted to a {@link RandomAccessFile}.
083         * @throws IOException                   if an I/O error occurs converting to an {@link RandomAccessFile} using {@link #getRandomAccessFile()}.
084         * @see AbstractOrigin#getFile()
085         * @see #getUnchecked()
086         */
087        @Override
088        public RandomAccessFileOutputStream get() throws IOException {
089            return new RandomAccessFileOutputStream(this);
090        }
091
092    }
093
094    /**
095     * Constructs a new {@link Builder}.
096     *
097     * @return a new {@link Builder}.
098     */
099    public static Builder builder() {
100        return new Builder();
101    }
102
103    private final RandomAccessFile randomAccessFile;
104
105    private RandomAccessFileOutputStream(final Builder builder) throws IOException {
106        this.randomAccessFile = builder.getRandomAccessFile();
107    }
108
109    @Override
110    public void close() throws IOException {
111        this.randomAccessFile.close();
112        super.close();
113    }
114
115    @SuppressWarnings("resource")
116    @Override
117    public void flush() throws IOException {
118        randomAccessFile.getChannel().force(true);
119        super.flush();
120    }
121
122    /**
123     * Gets the underlying random access file.
124     *
125     * @return the underlying random access file.
126     * @since 2.19.0
127     */
128    public RandomAccessFile getRandomAccessFile() {
129        return randomAccessFile;
130    }
131
132    @Override
133    public void write(final int b) throws IOException {
134        randomAccessFile.write(b);
135    }
136
137}