AbstractOrigin.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.build;

  18. import java.io.ByteArrayInputStream;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.InputStreamReader;
  24. import java.io.OutputStream;
  25. import java.io.OutputStreamWriter;
  26. import java.io.RandomAccessFile;
  27. import java.io.Reader;
  28. import java.io.Writer;
  29. import java.net.URI;
  30. import java.nio.charset.Charset;
  31. import java.nio.file.Files;
  32. import java.nio.file.OpenOption;
  33. import java.nio.file.Path;
  34. import java.nio.file.Paths;
  35. import java.nio.file.StandardOpenOption;
  36. import java.nio.file.spi.FileSystemProvider;
  37. import java.util.Arrays;
  38. import java.util.Objects;

  39. import org.apache.commons.io.IORandomAccessFile;
  40. import org.apache.commons.io.IOUtils;
  41. import org.apache.commons.io.RandomAccessFileMode;
  42. import org.apache.commons.io.RandomAccessFiles;
  43. import org.apache.commons.io.file.spi.FileSystemProviders;
  44. import org.apache.commons.io.input.BufferedFileChannelInputStream;
  45. import org.apache.commons.io.input.CharSequenceInputStream;
  46. import org.apache.commons.io.input.CharSequenceReader;
  47. import org.apache.commons.io.input.ReaderInputStream;
  48. import org.apache.commons.io.output.RandomAccessFileOutputStream;
  49. import org.apache.commons.io.output.WriterOutputStream;

  50. /**
  51.  * Abstracts the origin of data for builders like a {@link File}, {@link Path}, {@link Reader}, {@link Writer}, {@link InputStream}, {@link OutputStream}, and
  52.  * {@link URI}.
  53.  * <p>
  54.  * Some methods may throw {@link UnsupportedOperationException} if that method is not implemented in a concrete subclass, see {@link #getFile()} and
  55.  * {@link #getPath()}.
  56.  * </p>
  57.  *
  58.  * @param <T> the type of instances to build.
  59.  * @param <B> the type of builder subclass.
  60.  * @since 2.12.0
  61.  */
  62. public abstract class AbstractOrigin<T, B extends AbstractOrigin<T, B>> extends AbstractSupplier<T, B> {

  63.     /**
  64.      * A {@link RandomAccessFile} origin.
  65.      * <p>
  66.      * This origin cannot support File and Path since you cannot query a RandomAccessFile for those attributes; Use {@link IORandomAccessFileOrigin}
  67.      * instead.
  68.      * </p>
  69.      *
  70.      * @param <T> the type of instances to build.
  71.      * @param <B> the type of builder subclass.
  72.      */
  73.     public abstract static class AbstractRandomAccessFileOrigin<T extends RandomAccessFile, B extends AbstractRandomAccessFileOrigin<T, B>>
  74.             extends AbstractOrigin<T, B> {

  75.         /**
  76.          * A {@link RandomAccessFile} origin.
  77.          * <p>
  78.          * Starting from this origin, you can everything except a Path and a File.
  79.          * </p>
  80.          *
  81.          * @param origin The origin, not null.
  82.          */
  83.         public AbstractRandomAccessFileOrigin(final T origin) {
  84.             super(origin);
  85.         }

  86.         @Override
  87.         public byte[] getByteArray() throws IOException {
  88.             final long longLen = origin.length();
  89.             if (longLen > Integer.MAX_VALUE) {
  90.                 throw new IllegalStateException("Origin too large.");
  91.             }
  92.             return RandomAccessFiles.read(origin, 0, (int) longLen);
  93.         }

  94.         @Override
  95.         public byte[] getByteArray(final long position, final int length) throws IOException {
  96.             return RandomAccessFiles.read(origin, position, length);
  97.         }

  98.         @Override
  99.         public CharSequence getCharSequence(final Charset charset) throws IOException {
  100.             return new String(getByteArray(), charset);
  101.         }

  102.         @SuppressWarnings("resource")
  103.         @Override
  104.         public InputStream getInputStream(final OpenOption... options) throws IOException {
  105.             return BufferedFileChannelInputStream.builder().setFileChannel(origin.getChannel()).get();
  106.         }

  107.         @Override
  108.         public OutputStream getOutputStream(final OpenOption... options) throws IOException {
  109.             return RandomAccessFileOutputStream.builder().setRandomAccessFile(origin).get();
  110.         }

  111.         @Override
  112.         public T getRandomAccessFile(final OpenOption... openOption) {
  113.             // No conversion
  114.             return get();
  115.         }

  116.         @Override
  117.         public Reader getReader(final Charset charset) throws IOException {
  118.             return new InputStreamReader(getInputStream(), charset);
  119.         }

  120.         @Override
  121.         public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
  122.             return new OutputStreamWriter(getOutputStream(options), charset);
  123.         }

  124.         @Override
  125.         public long size() throws IOException {
  126.             return origin.length();
  127.         }
  128.     }

  129.     /**
  130.      * A {@code byte[]} origin.
  131.      */
  132.     public static class ByteArrayOrigin extends AbstractOrigin<byte[], ByteArrayOrigin> {

  133.         /**
  134.          * Constructs a new instance for the given origin.
  135.          *
  136.          * @param origin The origin, not null.
  137.          */
  138.         public ByteArrayOrigin(final byte[] origin) {
  139.             super(origin);
  140.         }

  141.         @Override
  142.         public byte[] getByteArray() {
  143.             // No conversion
  144.             return get();
  145.         }

  146.         /**
  147.          * {@inheritDoc}
  148.          * <p>
  149.          * The {@code options} parameter is ignored since a {@code byte[]} does not need an {@link OpenOption} to be read.
  150.          * </p>
  151.          */
  152.         @Override
  153.         public InputStream getInputStream(final OpenOption... options) throws IOException {
  154.             return new ByteArrayInputStream(origin);
  155.         }

  156.         @Override
  157.         public Reader getReader(final Charset charset) throws IOException {
  158.             return new InputStreamReader(getInputStream(), charset);
  159.         }

  160.         @Override
  161.         public long size() throws IOException {
  162.             return origin.length;
  163.         }

  164.     }

  165.     /**
  166.      * A {@link CharSequence} origin.
  167.      */
  168.     public static class CharSequenceOrigin extends AbstractOrigin<CharSequence, CharSequenceOrigin> {

  169.         /**
  170.          * Constructs a new instance for the given origin.
  171.          *
  172.          * @param origin The origin, not null.
  173.          */
  174.         public CharSequenceOrigin(final CharSequence origin) {
  175.             super(origin);
  176.         }

  177.         @Override
  178.         public byte[] getByteArray() {
  179.             // TODO Pass in a Charset? Consider if call sites actually need this.
  180.             return origin.toString().getBytes(Charset.defaultCharset());
  181.         }

  182.         /**
  183.          * {@inheritDoc}
  184.          * <p>
  185.          * The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
  186.          * </p>
  187.          */
  188.         @Override
  189.         public CharSequence getCharSequence(final Charset charset) {
  190.             // No conversion
  191.             return get();
  192.         }

  193.         /**
  194.          * {@inheritDoc}
  195.          * <p>
  196.          * The {@code options} parameter is ignored since a {@link CharSequence} does not need an {@link OpenOption} to be read.
  197.          * </p>
  198.          */
  199.         @Override
  200.         public InputStream getInputStream(final OpenOption... options) throws IOException {
  201.             // TODO Pass in a Charset? Consider if call sites actually need this.
  202.             return CharSequenceInputStream.builder().setCharSequence(getCharSequence(Charset.defaultCharset())).get();
  203.         }

  204.         /**
  205.          * {@inheritDoc}
  206.          * <p>
  207.          * The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
  208.          * </p>
  209.          */
  210.         @Override
  211.         public Reader getReader(final Charset charset) throws IOException {
  212.             return new CharSequenceReader(get());
  213.         }

  214.         @Override
  215.         public long size() throws IOException {
  216.             return origin.length();
  217.         }

  218.     }

  219.     /**
  220.      * A {@link File} origin.
  221.      * <p>
  222.      * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
  223.      * </p>
  224.      */
  225.     public static class FileOrigin extends AbstractOrigin<File, FileOrigin> {

  226.         /**
  227.          * Constructs a new instance for the given origin.
  228.          *
  229.          * @param origin The origin, not null.
  230.          */
  231.         public FileOrigin(final File origin) {
  232.             super(origin);
  233.         }

  234.         @Override
  235.         public byte[] getByteArray(final long position, final int length) throws IOException {
  236.             try (RandomAccessFile raf = RandomAccessFileMode.READ_ONLY.create(origin)) {
  237.                 return RandomAccessFiles.read(raf, position, length);
  238.             }
  239.         }

  240.         @Override
  241.         public File getFile() {
  242.             // No conversion
  243.             return get();
  244.         }

  245.         @Override
  246.         public Path getPath() {
  247.             return get().toPath();
  248.         }

  249.     }

  250.     /**
  251.      * An {@link InputStream} origin.
  252.      * <p>
  253.      * This origin cannot provide some of the other aspects.
  254.      * </p>
  255.      */
  256.     public static class InputStreamOrigin extends AbstractOrigin<InputStream, InputStreamOrigin> {

  257.         /**
  258.          * Constructs a new instance for the given origin.
  259.          *
  260.          * @param origin The origin, not null.
  261.          */
  262.         public InputStreamOrigin(final InputStream origin) {
  263.             super(origin);
  264.         }

  265.         @Override
  266.         public byte[] getByteArray() throws IOException {
  267.             return IOUtils.toByteArray(origin);
  268.         }

  269.         /**
  270.          * {@inheritDoc}
  271.          * <p>
  272.          * The {@code options} parameter is ignored since a {@link InputStream} does not need an {@link OpenOption} to be read.
  273.          * </p>
  274.          */
  275.         @Override
  276.         public InputStream getInputStream(final OpenOption... options) {
  277.             // No conversion
  278.             return get();
  279.         }

  280.         @Override
  281.         public Reader getReader(final Charset charset) throws IOException {
  282.             return new InputStreamReader(getInputStream(), charset);
  283.         }

  284.     }

  285.     /**
  286.      * A {@link IORandomAccessFile} origin.
  287.      *
  288.      * @since 2.18.0
  289.      */
  290.     public static class IORandomAccessFileOrigin extends AbstractRandomAccessFileOrigin<IORandomAccessFile, IORandomAccessFileOrigin> {

  291.         /**
  292.          * A {@link RandomAccessFile} origin.
  293.          *
  294.          * @param origin The origin, not null.
  295.          */
  296.         public IORandomAccessFileOrigin(final IORandomAccessFile origin) {
  297.             super(origin);
  298.         }

  299.         @SuppressWarnings("resource")
  300.         @Override
  301.         public File getFile() {
  302.             return get().getFile();
  303.         }

  304.         @Override
  305.         public Path getPath() {
  306.             return getFile().toPath();
  307.         }

  308.     }

  309.     /**
  310.      * An {@link OutputStream} origin.
  311.      * <p>
  312.      * This origin cannot provide some of the other aspects.
  313.      * </p>
  314.      */
  315.     public static class OutputStreamOrigin extends AbstractOrigin<OutputStream, OutputStreamOrigin> {

  316.         /**
  317.          * Constructs a new instance for the given origin.
  318.          *
  319.          * @param origin The origin, not null.
  320.          */
  321.         public OutputStreamOrigin(final OutputStream origin) {
  322.             super(origin);
  323.         }

  324.         /**
  325.          * {@inheritDoc}
  326.          * <p>
  327.          * The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
  328.          * </p>
  329.          */
  330.         @Override
  331.         public OutputStream getOutputStream(final OpenOption... options) {
  332.             // No conversion
  333.             return get();
  334.         }

  335.         /**
  336.          * {@inheritDoc}
  337.          * <p>
  338.          * The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
  339.          * </p>
  340.          */
  341.         @Override
  342.         public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
  343.             return new OutputStreamWriter(origin, charset);
  344.         }
  345.     }

  346.     /**
  347.      * A {@link Path} origin.
  348.      * <p>
  349.      * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
  350.      * </p>
  351.      */
  352.     public static class PathOrigin extends AbstractOrigin<Path, PathOrigin> {

  353.         /**
  354.          * Constructs a new instance for the given origin.
  355.          *
  356.          * @param origin The origin, not null.
  357.          */
  358.         public PathOrigin(final Path origin) {
  359.             super(origin);
  360.         }

  361.         @Override
  362.         public byte[] getByteArray(final long position, final int length) throws IOException {
  363.             return RandomAccessFileMode.READ_ONLY.apply(origin, raf -> RandomAccessFiles.read(raf, position, length));
  364.         }

  365.         @Override
  366.         public File getFile() {
  367.             return get().toFile();
  368.         }

  369.         @Override
  370.         public Path getPath() {
  371.             // No conversion
  372.             return get();
  373.         }

  374.     }

  375.     /**
  376.      * A {@link RandomAccessFile} origin.
  377.      * <p>
  378.      * This origin cannot support File and Path since you cannot query a RandomAccessFile for those attributes; Use {@link IORandomAccessFileOrigin}
  379.      * instead.
  380.      * </p>
  381.      */
  382.     public static class RandomAccessFileOrigin extends AbstractRandomAccessFileOrigin<RandomAccessFile, RandomAccessFileOrigin> {

  383.         /**
  384.          * A {@link RandomAccessFile} origin.
  385.          * <p>
  386.          * Starting from this origin, you can everything except a Path and a File.
  387.          * </p>
  388.          *
  389.          * @param origin The origin, not null.
  390.          */
  391.         public RandomAccessFileOrigin(final RandomAccessFile origin) {
  392.             super(origin);
  393.         }

  394.     }

  395.     /**
  396.      * A {@link Reader} origin.
  397.      * <p>
  398.      * This origin cannot provide conversions to other aspects.
  399.      * </p>
  400.      */
  401.     public static class ReaderOrigin extends AbstractOrigin<Reader, ReaderOrigin> {

  402.         /**
  403.          * Constructs a new instance for the given origin.
  404.          *
  405.          * @param origin The origin, not null.
  406.          */
  407.         public ReaderOrigin(final Reader origin) {
  408.             super(origin);
  409.         }

  410.         @Override
  411.         public byte[] getByteArray() throws IOException {
  412.             // TODO Pass in a Charset? Consider if call sites actually need this.
  413.             return IOUtils.toByteArray(origin, Charset.defaultCharset());
  414.         }

  415.         /**
  416.          * {@inheritDoc}
  417.          * <p>
  418.          * The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
  419.          * </p>
  420.          */
  421.         @Override
  422.         public CharSequence getCharSequence(final Charset charset) throws IOException {
  423.             return IOUtils.toString(origin);
  424.         }

  425.         /**
  426.          * {@inheritDoc}
  427.          * <p>
  428.          * The {@code options} parameter is ignored since a {@link Reader} does not need an {@link OpenOption} to be read.
  429.          * </p>
  430.          */
  431.         @Override
  432.         public InputStream getInputStream(final OpenOption... options) throws IOException {
  433.             // TODO Pass in a Charset? Consider if call sites actually need this.
  434.             return ReaderInputStream.builder().setReader(origin).setCharset(Charset.defaultCharset()).get();
  435.         }

  436.         /**
  437.          * {@inheritDoc}
  438.          * <p>
  439.          * The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
  440.          * </p>
  441.          */
  442.         @Override
  443.         public Reader getReader(final Charset charset) throws IOException {
  444.             // No conversion
  445.             return get();
  446.         }
  447.     }

  448.     /**
  449.      * A {@link URI} origin.
  450.      */
  451.     public static class URIOrigin extends AbstractOrigin<URI, URIOrigin> {

  452.         private static final String SCHEME_HTTPS = "https";
  453.         private static final String SCHEME_HTTP = "http";

  454.         /**
  455.          * Constructs a new instance for the given origin.
  456.          *
  457.          * @param origin The origin, not null.
  458.          */
  459.         public URIOrigin(final URI origin) {
  460.             super(origin);
  461.         }

  462.         @Override
  463.         public File getFile() {
  464.             return getPath().toFile();
  465.         }

  466.         @Override
  467.         public InputStream getInputStream(final OpenOption... options) throws IOException {
  468.             final URI uri = get();
  469.             final String scheme = uri.getScheme();
  470.             final FileSystemProvider fileSystemProvider = FileSystemProviders.installed().getFileSystemProvider(scheme);
  471.             if (fileSystemProvider != null) {
  472.                 return Files.newInputStream(fileSystemProvider.getPath(uri), options);
  473.             }
  474.             if (SCHEME_HTTP.equalsIgnoreCase(scheme) || SCHEME_HTTPS.equalsIgnoreCase(scheme)) {
  475.                 return uri.toURL().openStream();
  476.             }
  477.             return Files.newInputStream(getPath(), options);
  478.         }

  479.         @Override
  480.         public Path getPath() {
  481.             return Paths.get(get());
  482.         }
  483.     }

  484.     /**
  485.      * A {@link Writer} origin.
  486.      * <p>
  487.      * This origin cannot provide conversions to other aspects.
  488.      * </p>
  489.      */
  490.     public static class WriterOrigin extends AbstractOrigin<Writer, WriterOrigin> {

  491.         /**
  492.          * Constructs a new instance for the given origin.
  493.          *
  494.          * @param origin The origin, not null.
  495.          */
  496.         public WriterOrigin(final Writer origin) {
  497.             super(origin);
  498.         }

  499.         /**
  500.          * {@inheritDoc}
  501.          * <p>
  502.          * The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
  503.          * </p>
  504.          */
  505.         @Override
  506.         public OutputStream getOutputStream(final OpenOption... options) throws IOException {
  507.             // TODO Pass in a Charset? Consider if call sites actually need this.
  508.             return WriterOutputStream.builder().setWriter(origin).setCharset(Charset.defaultCharset()).get();
  509.         }

  510.         /**
  511.          * {@inheritDoc}
  512.          * <p>
  513.          * The {@code charset} parameter is ignored since a {@link Writer} does not need a {@link Charset} to be written.
  514.          * </p>
  515.          * <p>
  516.          * The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
  517.          * </p>
  518.          */
  519.         @Override
  520.         public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
  521.             // No conversion
  522.             return get();
  523.         }
  524.     }

  525.     /**
  526.      * The non-null origin.
  527.      */
  528.     final T origin;

  529.     /**
  530.      * Constructs a new instance for subclasses.
  531.      *
  532.      * @param origin The origin, not null.
  533.      */
  534.     protected AbstractOrigin(final T origin) {
  535.         this.origin = Objects.requireNonNull(origin, "origin");
  536.     }

  537.     /**
  538.      * Gets the origin.
  539.      *
  540.      * @return the origin.
  541.      */
  542.     @Override
  543.     public T get() {
  544.         return origin;
  545.     }

  546.     /**
  547.      * Gets this origin as a byte array, if possible.
  548.      *
  549.      * @return this origin as a byte array, if possible.
  550.      * @throws IOException                   if an I/O error occurs.
  551.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  552.      */
  553.     public byte[] getByteArray() throws IOException {
  554.         return Files.readAllBytes(getPath());
  555.     }

  556.     /**
  557.      * Gets a portion of this origin as a byte array, if possible.
  558.      *
  559.      * @param position the initial index of the range to be copied, inclusive.
  560.      * @param length   How many bytes to copy.
  561.      * @return this origin as a byte array, if possible.
  562.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  563.      * @throws ArithmeticException           if the {@code position} overflows an int
  564.      * @throws IOException                   if an I/O error occurs.
  565.      * @since 2.13.0
  566.      */
  567.     public byte[] getByteArray(final long position, final int length) throws IOException {
  568.         final byte[] bytes = getByteArray();
  569.         // Checks for int overflow.
  570.         final int start = Math.toIntExact(position);
  571.         if (start < 0 || length < 0 || start + length < 0 || start + length > bytes.length) {
  572.             throw new IllegalArgumentException("Couldn't read array (start: " + start + ", length: " + length + ", data length: " + bytes.length + ").");
  573.         }
  574.         return Arrays.copyOfRange(bytes, start, start + length);
  575.     }

  576.     /**
  577.      * Gets this origin as a byte array, if possible.
  578.      *
  579.      * @param charset The charset to use if conversion from bytes is needed.
  580.      * @return this origin as a byte array, if possible.
  581.      * @throws IOException                   if an I/O error occurs.
  582.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  583.      */
  584.     public CharSequence getCharSequence(final Charset charset) throws IOException {
  585.         return new String(getByteArray(), charset);
  586.     }

  587.     /**
  588.      * Gets this origin as a Path, if possible.
  589.      *
  590.      * @return this origin as a Path, if possible.
  591.      * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
  592.      */
  593.     public File getFile() {
  594.         throw new UnsupportedOperationException(
  595.                 String.format("%s#getFile() for %s origin %s", getSimpleClassName(), origin.getClass().getSimpleName(), origin));
  596.     }

  597.     /**
  598.      * Gets this origin as an InputStream, if possible.
  599.      *
  600.      * @param options options specifying how the file is opened
  601.      * @return this origin as an InputStream, if possible.
  602.      * @throws IOException                   if an I/O error occurs.
  603.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  604.      */
  605.     public InputStream getInputStream(final OpenOption... options) throws IOException {
  606.         return Files.newInputStream(getPath(), options);
  607.     }

  608.     /**
  609.      * Gets this origin as an OutputStream, if possible.
  610.      *
  611.      * @param options options specifying how the file is opened
  612.      * @return this origin as an OutputStream, if possible.
  613.      * @throws IOException                   if an I/O error occurs.
  614.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  615.      */
  616.     public OutputStream getOutputStream(final OpenOption... options) throws IOException {
  617.         return Files.newOutputStream(getPath(), options);
  618.     }

  619.     /**
  620.      * Gets this origin as a Path, if possible.
  621.      *
  622.      * @return this origin as a Path, if possible.
  623.      * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
  624.      */
  625.     public Path getPath() {
  626.         throw new UnsupportedOperationException(
  627.                 String.format("%s#getPath() for %s origin %s", getSimpleClassName(), origin.getClass().getSimpleName(), origin));
  628.     }

  629.     /**
  630.      * Gets this origin as a RandomAccessFile, if possible.
  631.      *
  632.      * @param openOption options like {@link StandardOpenOption}.
  633.      * @return this origin as a RandomAccessFile, if possible.
  634.      * @throws FileNotFoundException         See {@link RandomAccessFile#RandomAccessFile(File, String)}.
  635.      * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
  636.      * @since 2.18.0
  637.      */
  638.     public RandomAccessFile getRandomAccessFile(final OpenOption... openOption) throws FileNotFoundException {
  639.         return RandomAccessFileMode.valueOf(openOption).create(getFile());
  640.     }

  641.     /**
  642.      * Gets a new Reader on the origin, buffered by default.
  643.      *
  644.      * @param charset the charset to use for decoding
  645.      * @return a new Reader on the origin.
  646.      * @throws IOException if an I/O error occurs opening the file.
  647.      */
  648.     public Reader getReader(final Charset charset) throws IOException {
  649.         return Files.newBufferedReader(getPath(), charset);
  650.     }

  651.     private String getSimpleClassName() {
  652.         return getClass().getSimpleName();
  653.     }

  654.     /**
  655.      * Gets a new Writer on the origin, buffered by default.
  656.      *
  657.      * @param charset the charset to use for encoding
  658.      * @param options options specifying how the file is opened
  659.      * @return a new Writer on the origin.
  660.      * @throws IOException                   if an I/O error occurs opening or creating the file.
  661.      * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
  662.      */
  663.     public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
  664.         return Files.newBufferedWriter(getPath(), charset, options);
  665.     }

  666.     /**
  667.      * Gets the size of the origin, if possible.
  668.      *
  669.      * @return the size of the origin in bytes or characters.
  670.      * @throws IOException if an I/O error occurs.
  671.      * @since 2.13.0
  672.      */
  673.     public long size() throws IOException {
  674.         return Files.size(getPath());
  675.     }

  676.     @Override
  677.     public String toString() {
  678.         return getSimpleClassName() + "[" + origin.toString() + "]";
  679.     }
  680. }