RandomAccessFileInputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.io.input;

  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.RandomAccessFile;
  21. import java.util.Objects;

  22. import org.apache.commons.io.build.AbstractOrigin;
  23. import org.apache.commons.io.build.AbstractStreamBuilder;

  24. /**
  25.  * Streams data from a {@link RandomAccessFile} starting at its current position.
  26.  * <p>
  27.  * To build an instance, use {@link Builder}.
  28.  * </p>
  29.  *
  30.  * @see Builder
  31.  * @since 2.8.0
  32.  */
  33. public class RandomAccessFileInputStream extends AbstractInputStream {

  34.     // @formatter:off
  35.     /**
  36.      * Builds a new {@link RandomAccessFileInputStream}.
  37.      *
  38.      * <p>
  39.      * For example:
  40.      * </p>
  41.      * <pre>{@code
  42.      * RandomAccessFileInputStream s = RandomAccessFileInputStream.builder()
  43.      *   .setPath(path)
  44.      *   .setCloseOnClose(true)
  45.      *   .get();}
  46.      * </pre>
  47.      *
  48.      * @see #get()
  49.      * @since 2.12.0
  50.      */
  51.     // @formatter:on
  52.     public static class Builder extends AbstractStreamBuilder<RandomAccessFileInputStream, Builder> {

  53.         // private RandomAccessFile randomAccessFile;
  54.         private boolean propagateClose;

  55.         /**
  56.          * Builds a new {@link RandomAccessFileInputStream}.
  57.          * <p>
  58.          * You must set input that supports {@link RandomAccessFile} or {@link File}, otherwise, this method throws an exception. Only set one of
  59.          * RandomAccessFile or an origin that can be converted to a File.
  60.          * </p>
  61.          * <p>
  62.          * This builder use the following aspects:
  63.          * </p>
  64.          * <ul>
  65.          * <li>{@link RandomAccessFile}</li>
  66.          * <li>{@link File}</li>
  67.          * <li>closeOnClose</li>
  68.          * </ul>
  69.          *
  70.          * @return a new instance.
  71.          * @throws IllegalStateException         if the {@code origin} is {@code null}.
  72.          * @throws IllegalStateException         if both RandomAccessFile and origin are set.
  73.          * @throws UnsupportedOperationException if the origin cannot be converted to a {@link File}.
  74.          * @see AbstractOrigin#getFile()
  75.          */
  76.         @SuppressWarnings("resource") // Caller closes depending on settings
  77.         @Override
  78.         public RandomAccessFileInputStream get() throws IOException {
  79.             return new RandomAccessFileInputStream(getRandomAccessFile(), propagateClose);
  80.         }

  81.         /**
  82.          * Sets whether to close the underlying file when this stream is closed.
  83.          *
  84.          * @param propagateClose Whether to close the underlying file when this stream is closed.
  85.          * @return {@code this} instance.
  86.          */
  87.         public Builder setCloseOnClose(final boolean propagateClose) {
  88.             this.propagateClose = propagateClose;
  89.             return this;
  90.         }

  91.         /**
  92.          * Sets the RandomAccessFile to stream.
  93.          *
  94.          * @param randomAccessFile the RandomAccessFile to stream.
  95.          * @return {@code this} instance.
  96.          */
  97.         @Override // MUST keep this method for binary compatibility since the super version of this method uses a generic which compiles to Object.
  98.         public Builder setRandomAccessFile(final RandomAccessFile randomAccessFile) { // NOPMD see above.
  99.             return super.setRandomAccessFile(randomAccessFile);
  100.         }

  101.     }

  102.     /**
  103.      * Constructs a new {@link Builder}.
  104.      *
  105.      * @return a new {@link Builder}.
  106.      * @since 2.12.0
  107.      */
  108.     public static Builder builder() {
  109.         return new Builder();
  110.     }

  111.     private final boolean propagateClose;
  112.     private final RandomAccessFile randomAccessFile;

  113.     /**
  114.      * Constructs a new instance configured to leave the underlying file open when this stream is closed.
  115.      *
  116.      * @param file The file to stream.
  117.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  118.      */
  119.     @Deprecated
  120.     public RandomAccessFileInputStream(final RandomAccessFile file) {
  121.         this(file, false);
  122.     }

  123.     /**
  124.      * Constructs a new instance.
  125.      *
  126.      * @param file         The file to stream.
  127.      * @param propagateClose Whether to close the underlying file when this stream is closed.
  128.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  129.      */
  130.     @Deprecated
  131.     public RandomAccessFileInputStream(final RandomAccessFile file, final boolean propagateClose) {
  132.         this.randomAccessFile = Objects.requireNonNull(file, "file");
  133.         this.propagateClose = propagateClose;
  134.     }

  135.     /**
  136.      * Gets an estimate of the number of bytes that can be read (or skipped over) from this input stream.
  137.      *
  138.      * If there are more than {@link Integer#MAX_VALUE} bytes available, return {@link Integer#MAX_VALUE}.
  139.      *
  140.      * @return An estimate of the number of bytes that can be read.
  141.      * @throws IOException If an I/O error occurs.
  142.      */
  143.     @Override
  144.     public int available() throws IOException {
  145.         final long avail = availableLong();
  146.         if (avail > Integer.MAX_VALUE) {
  147.             return Integer.MAX_VALUE;
  148.         }
  149.         return (int) avail;
  150.     }

  151.     /**
  152.      * Gets the number of bytes that can be read (or skipped over) from this input stream.
  153.      *
  154.      * @return The number of bytes that can be read.
  155.      * @throws IOException If an I/O error occurs.
  156.      */
  157.     public long availableLong() throws IOException {
  158.         return isClosed() ? 0 : randomAccessFile.length() - randomAccessFile.getFilePointer();
  159.     }

  160.     @Override
  161.     public void close() throws IOException {
  162.         super.close();
  163.         if (propagateClose) {
  164.             randomAccessFile.close();
  165.         }
  166.     }

  167.     /**
  168.      * Gets the underlying file.
  169.      *
  170.      * @return the underlying file.
  171.      */
  172.     public RandomAccessFile getRandomAccessFile() {
  173.         return randomAccessFile;
  174.     }

  175.     /**
  176.      * Tests whether to close the underlying file when this stream is closed.
  177.      *
  178.      * @return Whether to close the underlying file when this stream is closed.
  179.      */
  180.     public boolean isCloseOnClose() {
  181.         return propagateClose;
  182.     }

  183.     @Override
  184.     public int read() throws IOException {
  185.         return randomAccessFile.read();
  186.     }

  187.     @Override
  188.     public int read(final byte[] bytes) throws IOException {
  189.         return randomAccessFile.read(bytes);
  190.     }

  191.     @Override
  192.     public int read(final byte[] bytes, final int offset, final int length) throws IOException {
  193.         return randomAccessFile.read(bytes, offset, length);
  194.     }

  195.     @Override
  196.     public long skip(final long skipCount) throws IOException {
  197.         if (skipCount <= 0) {
  198.             return 0;
  199.         }
  200.         final long filePointer = randomAccessFile.getFilePointer();
  201.         final long fileLength = randomAccessFile.length();
  202.         if (filePointer >= fileLength) {
  203.             return 0;
  204.         }
  205.         final long targetPos = filePointer + skipCount;
  206.         final long newPos = targetPos > fileLength ? fileLength - 1 : targetPos;
  207.         if (newPos > 0) {
  208.             randomAccessFile.seek(newPos);
  209.         }
  210.         return randomAccessFile.getFilePointer() - filePointer;
  211.     }
  212. }