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 *      https://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.build;
019
020import java.io.ByteArrayInputStream;
021import java.io.Closeable;
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileNotFoundException;
025import java.io.IOException;
026import java.io.InputStream;
027import java.io.InputStreamReader;
028import java.io.OutputStream;
029import java.io.OutputStreamWriter;
030import java.io.RandomAccessFile;
031import java.io.Reader;
032import java.io.Writer;
033import java.net.URI;
034import java.net.URLConnection;
035import java.nio.channels.Channel;
036import java.nio.channels.Channels;
037import java.nio.channels.FileChannel;
038import java.nio.channels.ReadableByteChannel;
039import java.nio.channels.SeekableByteChannel;
040import java.nio.channels.WritableByteChannel;
041import java.nio.charset.Charset;
042import java.nio.file.Files;
043import java.nio.file.OpenOption;
044import java.nio.file.Path;
045import java.nio.file.Paths;
046import java.nio.file.StandardOpenOption;
047import java.time.Duration;
048import java.util.Arrays;
049import java.util.Objects;
050import java.util.stream.Stream;
051
052import org.apache.commons.io.Charsets;
053import org.apache.commons.io.IORandomAccessFile;
054import org.apache.commons.io.IOUtils;
055import org.apache.commons.io.RandomAccessFileMode;
056import org.apache.commons.io.RandomAccessFiles;
057import org.apache.commons.io.channels.ByteArraySeekableByteChannel;
058import org.apache.commons.io.input.BufferedFileChannelInputStream;
059import org.apache.commons.io.input.CharSequenceInputStream;
060import org.apache.commons.io.input.CharSequenceReader;
061import org.apache.commons.io.input.ReaderInputStream;
062import org.apache.commons.io.output.RandomAccessFileOutputStream;
063import org.apache.commons.io.output.WriterOutputStream;
064
065/**
066 * Abstracts and wraps an <em>origin</em> for builders, where an origin is a {@code byte[]}, {@link Channel}, {@link CharSequence}, {@link File},
067 * {@link InputStream}, {@link IORandomAccessFile}, {@link OutputStream}, {@link Path}, {@link RandomAccessFile}, {@link Reader}, {@link URI},
068 * or {@link Writer}.
069 * <p>
070 * An origin represents where bytes/characters come from or go to. Concrete subclasses
071 * expose only the operations that make sense for the underlying source or sink; invoking an unsupported operation
072 * results in {@link UnsupportedOperationException} (see, for example, {@link #getFile()} and {@link #getPath()}).
073 * </p>
074 * <p>
075 * An instance doesn't own its origin, it holds on to it to allow conversions. There are two use cases related to resource management for a Builder:
076 * </p>
077 * <ul>
078 * <li>
079 * A client allocates a {@linkplain Closeable} (or {@linkplain AutoCloseable}) resource, creates a Builder, and gives the Builder that resource by calling a
080 * setter method. No matter what happens next, the client is responsible for releasing the resource ({@code Closeable.close()}). In this case, the origin
081 * wraps but doesn't own the closeable resource. There is no transfer of ownership.
082 * </li>
083 * <li>
084 * A client creates a Builder and gives it a non-Closeable object, like a File or a Path. The client then calls the Builder's factory method
085 * (like {@linkplain #get()}), and that call returns a Closeable or a resource that requires releasing in some other way. No matter what happens next, the
086 * client is responsible for releasing that resource. In this case, the origin doesn't wrap a closeable resource.
087 * </li>
088 * </ul>
089 * <p>
090 * In both cases, the client causes the allocation and is responsible for releasing the resource.
091 * </p>
092 * <p>
093 * The table below summarizes which views and conversions are supported for each origin type.
094 * Column headers show the target view; cells indicate whether that view is available from the origin in that row.
095 * </p>
096 *
097 * <table>
098 *   <caption>Supported Conversions</caption>
099 *   <thead>
100 *     <tr>
101 *       <th>Origin Type</th>
102 *       <th>byte[]</th>
103 *       <th>CS</th>
104 *       <th>File</th>
105 *       <th>Path</th>
106 *       <th>RAF</th>
107 *       <th>IS</th>
108 *       <th>Reader</th>
109 *       <th>RBC</th>
110 *       <th>OS</th>
111 *       <th>Writer</th>
112 *       <th>WBC</th>
113 *       <th>Channel type<sup>2</sup></th>
114 *     </tr>
115 *   </thead>
116 *   <tbody>
117 *     <tr>
118 *       <td>byte[]</td>
119 *       <td>✔</td>
120 *       <td>✔</td>
121 *       <td>✖</td>
122 *       <td>✖</td>
123 *       <td>✖</td>
124 *       <td>✔</td>
125 *       <td>✔</td>
126 *       <td>✔</td>
127 *       <td>✖</td>
128 *       <td>✖</td>
129 *       <td>✖</td>
130 *       <td>SBC</td>
131 *     </tr>
132 *     <tr>
133 *       <td>{@link CharSequence} (CS)</td>
134 *       <td>✔</td>
135 *       <td>✔</td>
136 *       <td>✖</td>
137 *       <td>✖</td>
138 *       <td>✖</td>
139 *       <td>✔<sup>1</sup></td>
140 *       <td>✔</td>
141 *       <td>✔<sup>1</sup></td>
142 *       <td>✖</td>
143 *       <td>✖</td>
144 *       <td>✖</td>
145 *       <td>SBC</td>
146 *     </tr>
147 *     <tr>
148 *       <td>{@link File}</td>
149 *       <td>✔</td>
150 *       <td>✔</td>
151 *       <td>✔</td>
152 *       <td>✔</td>
153 *       <td>✔</td>
154 *       <td>✔</td>
155 *       <td>✔</td>
156 *       <td>✔</td>
157 *       <td>✔</td>
158 *       <td>✔</td>
159 *       <td>✔</td>
160 *       <td>FC</td>
161 *     </tr>
162 *     <tr>
163 *       <td>{@link Path}</td>
164 *       <td>✔</td>
165 *       <td>✔</td>
166 *       <td>✔</td>
167 *       <td>✔</td>
168 *       <td>✔</td>
169 *       <td>✔</td>
170 *       <td>✔</td>
171 *       <td>✔</td>
172 *       <td>✔</td>
173 *       <td>✔</td>
174 *       <td>✔</td>
175 *       <td>FC</td>
176 *     </tr>
177 *     <tr>
178 *       <td>{@link IORandomAccessFile}</td>
179 *       <td>✔</td>
180 *       <td>✔</td>
181 *       <td>✔</td>
182 *       <td>✔</td>
183 *       <td>✔</td>
184 *       <td>✔</td>
185 *       <td>✔</td>
186 *       <td>✔</td>
187 *       <td>✔</td>
188 *       <td>✔</td>
189 *       <td>✔</td>
190 *       <td>FC</td>
191 *     </tr>
192 *     <tr>
193 *       <td>{@link RandomAccessFile} (RAF)</td>
194 *       <td>✔</td>
195 *       <td>✔</td>
196 *       <td>✖</td>
197 *       <td>✖</td>
198 *       <td>✔</td>
199 *       <td>✔</td>
200 *       <td>✔</td>
201 *       <td>✔</td>
202 *       <td>✔</td>
203 *       <td>✔</td>
204 *       <td>✔</td>
205 *       <td>FC</td>
206 *     </tr>
207 *     <tr>
208 *       <td>{@link InputStream} (IS)</td>
209 *       <td>✔</td>
210 *       <td>✔</td>
211 *       <td>✖</td>
212 *       <td>✖</td>
213 *       <td>✖</td>
214 *       <td>✔</td>
215 *       <td>✔</td>
216 *       <td>✔</td>
217 *       <td>✖</td>
218 *       <td>✖</td>
219 *       <td>✖</td>
220 *       <td>RBC</td>
221 *     </tr>
222 *     <tr>
223 *       <td>{@link Reader}</td>
224 *       <td>✔</td>
225 *       <td>✔</td>
226 *       <td>✖</td>
227 *       <td>✖</td>
228 *       <td>✖</td>
229 *       <td>✔<sup>1</sup></td>
230 *       <td>✔</td>
231 *       <td>✔<sup>1</sup></td>
232 *       <td>✖</td>
233 *       <td>✖</td>
234 *       <td>✖</td>
235 *       <td>RBC</td>
236 *     </tr>
237 *     <tr>
238 *       <td>{@link ReadableByteChannel} (RBC)</td>
239 *       <td>✔</td>
240 *       <td>✔</td>
241 *       <td>✖</td>
242 *       <td>✖</td>
243 *       <td>✖</td>
244 *       <td>✔</td>
245 *       <td>✔</td>
246 *       <td>✔</td>
247 *       <td>✖</td>
248 *       <td>✖</td>
249 *       <td>✖</td>
250 *       <td>RBC</td>
251 *     </tr>
252 *     <tr>
253 *       <td>{@link OutputStream} (OS)</td>
254 *       <td>✖</td>
255 *       <td>✖</td>
256 *       <td>✖</td>
257 *       <td>✖</td>
258 *       <td>✖</td>
259 *       <td>✖</td>
260 *       <td>✖</td>
261 *       <td>✖</td>
262 *       <td>✔</td>
263 *       <td>✔</td>
264 *       <td>✔</td>
265 *       <td>WBC</td>
266 *     </tr>
267 *     <tr>
268 *       <td>{@link Writer}</td>
269 *       <td>✖</td>
270 *       <td>✖</td>
271 *       <td>✖</td>
272 *       <td>✖</td>
273 *       <td>✖</td>
274 *       <td>✖</td>
275 *       <td>✖</td>
276 *       <td>✖</td>
277 *       <td>✔<sup>1</sup></td>
278 *       <td>✔</td>
279 *       <td>✔<sup>1</sup></td>
280 *       <td>WBC</td>
281 *     </tr>
282 *     <tr>
283 *       <td>{@link WritableByteChannel} (WBC)</td>
284 *       <td>✖</td>
285 *       <td>✖</td>
286 *       <td>✖</td>
287 *       <td>✖</td>
288 *       <td>✖</td>
289 *       <td>✖</td>
290 *       <td>✖</td>
291 *       <td>✖</td>
292 *       <td>✔</td>
293 *       <td>✔</td>
294 *       <td>✔</td>
295 *       <td>WBC</td>
296 *     </tr>
297 *     <tr>
298 *       <td>{@link URI} (FileSystem)</td>
299 *       <td>✔</td>
300 *       <td>✔</td>
301 *       <td>✔</td>
302 *       <td>✔</td>
303 *       <td>✔</td>
304 *       <td>✔</td>
305 *       <td>✔</td>
306 *       <td>✔</td>
307 *       <td>✔</td>
308 *       <td>✔</td>
309 *       <td>✔</td>
310 *       <td>FC</td>
311 *     </tr>
312 *     <tr>
313 *       <td>{@link URI} (http/https)</td>
314 *       <td>✔</td>
315 *       <td>✔</td>
316 *       <td>✖</td>
317 *       <td>✖</td>
318 *       <td>✖</td>
319 *       <td>✔</td>
320 *       <td>✔</td>
321 *       <td>✔</td>
322 *       <td>✖</td>
323 *       <td>✖</td>
324 *       <td>✖</td>
325 *       <td>RBC</td>
326 *     </tr>
327 *   </tbody>
328 * </table>
329 *
330 * <p><strong>Legend</strong></p>
331 * <ul>
332 *   <li>✔ = Supported</li>
333 *   <li>✖ = Not supported (throws {@link UnsupportedOperationException})</li>
334 *   <li><sup>1</sup> = Characters are converted to bytes using the default {@link Charset}.</li>
335 *   <li><sup>2</sup> Minimum channel type provided by the origin:
336 *     <ul>
337 *         <li>RBC = {@linkplain ReadableByteChannel}</li>
338 *         <li>WBC = {@linkplain WritableByteChannel}</li>
339 *         <li>SBC = {@linkplain SeekableByteChannel}</li>
340 *         <li>FC = {@linkplain FileChannel}</li>
341 *     </ul>
342 *     The exact channel type may be a subtype of the minimum shown.
343 *   </li>
344 * </ul>
345 *
346 * @param <T> the type produced by the builder.
347 * @param <B> the concrete builder subclass type.
348 * @since 2.12.0
349 */
350public abstract class AbstractOrigin<T, B extends AbstractOrigin<T, B>> extends AbstractSupplier<T, B> {
351
352    /**
353     * A {@link RandomAccessFile} origin.
354     * <p>
355     * This origin cannot support File and Path since you cannot query a RandomAccessFile for those attributes; Use {@link IORandomAccessFileOrigin}
356     * instead.
357     * </p>
358     *
359     * @param <T> the type of instances to build.
360     * @param <B> the type of builder subclass.
361     */
362    public abstract static class AbstractRandomAccessFileOrigin<T extends RandomAccessFile, B extends AbstractRandomAccessFileOrigin<T, B>>
363            extends AbstractOrigin<T, B> {
364
365        /**
366         * A {@link RandomAccessFile} origin.
367         * <p>
368         * Starting from this origin, you can everything except a Path and a File.
369         * </p>
370         *
371         * @param origin The origin, not null.
372         * @throws NullPointerException if {@code origin} is {@code null}.
373         */
374        public AbstractRandomAccessFileOrigin(final T origin) {
375            super(origin);
376        }
377
378        @Override
379        public byte[] getByteArray() throws IOException {
380            final long longLen = origin.length();
381            if (longLen > Integer.MAX_VALUE) {
382                throw new IllegalStateException("Origin too large.");
383            }
384            return RandomAccessFiles.read(origin, 0, (int) longLen);
385        }
386
387        @Override
388        public byte[] getByteArray(final long position, final int length) throws IOException {
389            return RandomAccessFiles.read(origin, position, length);
390        }
391
392        @Override
393        protected Channel getChannel(final OpenOption... options) throws IOException {
394            return getRandomAccessFile(options).getChannel();
395        }
396
397        @Override
398        public CharSequence getCharSequence(final Charset charset) throws IOException {
399            return new String(getByteArray(), charset);
400        }
401
402        @SuppressWarnings("resource")
403        @Override
404        public InputStream getInputStream(final OpenOption... options) throws IOException {
405            return BufferedFileChannelInputStream.builder().setFileChannel(origin.getChannel()).get();
406        }
407
408        @Override
409        public OutputStream getOutputStream(final OpenOption... options) throws IOException {
410            return RandomAccessFileOutputStream.builder().setRandomAccessFile(origin).get();
411        }
412
413        @Override
414        public T getRandomAccessFile(final OpenOption... openOption) {
415            // No conversion
416            return get();
417        }
418
419        @Override
420        public Reader getReader(final Charset charset) throws IOException {
421            return new InputStreamReader(getInputStream(), Charsets.toCharset(charset));
422        }
423
424        @Override
425        public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
426            return new OutputStreamWriter(getOutputStream(options), Charsets.toCharset(charset));
427        }
428
429        @Override
430        public long size() throws IOException {
431            return origin.length();
432        }
433    }
434
435    /**
436     * A {@code byte[]} origin.
437     */
438    public static class ByteArrayOrigin extends AbstractOrigin<byte[], ByteArrayOrigin> {
439
440        /**
441         * Constructs a new instance for the given origin.
442         *
443         * @param origin The origin, not null.
444         * @throws NullPointerException if {@code origin} is {@code null}.
445         */
446        public ByteArrayOrigin(final byte[] origin) {
447            super(origin);
448        }
449
450        /**
451         * {@inheritDoc}
452         *
453         * <p>
454         * No conversion should occur when calling this method.
455         * </p>
456         */
457        @Override
458        public byte[] getByteArray() {
459            // No conversion
460            return get();
461        }
462
463        @Override
464        protected Channel getChannel(final OpenOption... options) throws IOException {
465            for (final OpenOption option : options) {
466                if (option == StandardOpenOption.WRITE) {
467                    throw new UnsupportedOperationException("Only READ is supported for byte[] origins: " + Arrays.toString(options));
468                }
469            }
470            return ByteArraySeekableByteChannel.wrap(getByteArray());
471        }
472
473        /**
474         * {@inheritDoc}
475         * <p>
476         * The {@code options} parameter is ignored since a {@code byte[]} does not need an {@link OpenOption} to be read.
477         * </p>
478         */
479        @Override
480        public InputStream getInputStream(final OpenOption... options) throws IOException {
481            return new ByteArrayInputStream(origin);
482        }
483
484        @Override
485        public Reader getReader(final Charset charset) throws IOException {
486            return new InputStreamReader(getInputStream(), Charsets.toCharset(charset));
487        }
488
489        @Override
490        public long size() throws IOException {
491            return origin.length;
492        }
493
494    }
495
496    /**
497     * A {@link Channel} origin.
498     *
499     * @since 2.21.0
500     */
501    public static class ChannelOrigin extends AbstractOrigin<Channel, ChannelOrigin> {
502
503        /**
504         * Constructs a new instance for the given origin.
505         *
506         * @param origin The origin, not null.
507         * @throws NullPointerException if {@code origin} is {@code null}.
508         */
509        public ChannelOrigin(final Channel origin) {
510            super(origin);
511        }
512
513        @Override
514        public byte[] getByteArray() throws IOException {
515            return IOUtils.toByteArray(getInputStream());
516        }
517
518        /**
519         * {@inheritDoc}
520         *
521         * <p>
522         * No conversion should occur when calling this method.
523         * </p>
524         */
525        @Override
526        protected Channel getChannel(final OpenOption... options) throws IOException {
527            // No conversion
528            return get();
529        }
530
531        @Override
532        public InputStream getInputStream(final OpenOption... options) throws IOException {
533            return Channels.newInputStream(getChannel(ReadableByteChannel.class, options));
534        }
535
536        @Override
537        public OutputStream getOutputStream(final OpenOption... options) throws IOException {
538            return Channels.newOutputStream(getChannel(WritableByteChannel.class, options));
539        }
540
541        @Override
542        public Reader getReader(final Charset charset) throws IOException {
543            return Channels.newReader(
544                    getChannel(ReadableByteChannel.class),
545                    Charsets.toCharset(charset).newDecoder(),
546                    -1);
547        }
548
549        @Override
550        public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
551            return Channels.newWriter(getChannel(WritableByteChannel.class, options), Charsets.toCharset(charset).newEncoder(), -1);
552        }
553
554        @Override
555        public long size() throws IOException {
556            if (origin instanceof SeekableByteChannel) {
557                return ((SeekableByteChannel) origin).size();
558            }
559            throw unsupportedOperation("size");
560        }
561    }
562
563    /**
564     * A {@link CharSequence} origin.
565     */
566    public static class CharSequenceOrigin extends AbstractOrigin<CharSequence, CharSequenceOrigin> {
567
568        /**
569         * Constructs a new instance for the given origin.
570         *
571         * @param origin The origin, not null.
572         * @throws NullPointerException if {@code origin} is {@code null}.
573         */
574        public CharSequenceOrigin(final CharSequence origin) {
575            super(origin);
576        }
577
578        @Override
579        public byte[] getByteArray() {
580            // TODO Pass in a Charset? Consider if call sites actually need this.
581            return origin.toString().getBytes(Charset.defaultCharset());
582        }
583
584        @Override
585        protected Channel getChannel(final OpenOption... options) throws IOException {
586            for (final OpenOption option : options) {
587                if (option == StandardOpenOption.WRITE) {
588                    throw new UnsupportedOperationException("Only READ is supported for CharSequence origins: " + Arrays.toString(options));
589                }
590            }
591            return ByteArraySeekableByteChannel.wrap(getByteArray());
592        }
593
594        /**
595         * {@inheritDoc}
596         * <p>
597         * The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
598         * </p>
599         * <p>
600         * No conversion should occur when calling this method.
601         * </p>
602         */
603        @Override
604        public CharSequence getCharSequence(final Charset charset) {
605            // No conversion
606            return get();
607        }
608
609        /**
610         * {@inheritDoc}
611         * <p>
612         * The {@code options} parameter is ignored since a {@link CharSequence} does not need an {@link OpenOption} to be read.
613         * </p>
614         */
615        @Override
616        public InputStream getInputStream(final OpenOption... options) throws IOException {
617            // TODO Pass in a Charset? Consider if call sites actually need this.
618            return CharSequenceInputStream.builder().setCharSequence(getCharSequence(Charset.defaultCharset())).get();
619        }
620
621        /**
622         * {@inheritDoc}
623         * <p>
624         * The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
625         * </p>
626         */
627        @Override
628        public Reader getReader(final Charset charset) throws IOException {
629            return new CharSequenceReader(get());
630        }
631
632        @Override
633        public long size() throws IOException {
634            return origin.length();
635        }
636
637    }
638
639    /**
640     * A {@link File} origin.
641     * <p>
642     * 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.
643     * </p>
644     */
645    public static class FileOrigin extends AbstractOrigin<File, FileOrigin> {
646
647        /**
648         * Constructs a new instance for the given origin.
649         *
650         * @param origin The origin, not null.
651         * @throws NullPointerException if {@code origin} is {@code null}.
652         */
653        public FileOrigin(final File origin) {
654            super(origin);
655        }
656
657        @Override
658        public byte[] getByteArray(final long position, final int length) throws IOException {
659            try (RandomAccessFile raf = RandomAccessFileMode.READ_ONLY.create(origin)) {
660                return RandomAccessFiles.read(raf, position, length);
661            }
662        }
663
664        @Override
665        protected Channel getChannel(final OpenOption... options) throws IOException {
666            return Files.newByteChannel(getPath(), options);
667        }
668
669        /**
670         * {@inheritDoc}
671         *
672         * <p>
673         * No conversion should occur when calling this method.
674         * </p>
675         */
676        @Override
677        public File getFile() {
678            // No conversion
679            return get();
680        }
681
682        @Override
683        public Path getPath() {
684            return get().toPath();
685        }
686    }
687
688    /**
689     * An {@link InputStream} origin.
690     * <p>
691     * This origin cannot provide some of the other aspects.
692     * </p>
693     */
694    public static class InputStreamOrigin extends AbstractOrigin<InputStream, InputStreamOrigin> {
695
696        /**
697         * Constructs a new instance for the given origin.
698         *
699         * @param origin The origin, not null.
700         * @throws NullPointerException if {@code origin} is {@code null}.
701         */
702        public InputStreamOrigin(final InputStream origin) {
703            super(origin);
704        }
705
706        @Override
707        public byte[] getByteArray() throws IOException {
708            return IOUtils.toByteArray(origin);
709        }
710
711        @Override
712        protected Channel getChannel(final OpenOption... options) throws IOException {
713            return Channels.newChannel(getInputStream(options));
714        }
715
716        /**
717         * {@inheritDoc}
718         * <p>
719         * The {@code options} parameter is ignored since a {@link InputStream} does not need an {@link OpenOption} to be read.
720         * </p>
721         * <p>
722         * No conversion should occur when calling this method.
723         * </p>
724         */
725        @Override
726        public InputStream getInputStream(final OpenOption... options) {
727            // No conversion
728            return get();
729        }
730
731        @Override
732        public Reader getReader(final Charset charset) throws IOException {
733            return new InputStreamReader(getInputStream(), Charsets.toCharset(charset));
734        }
735
736        @Override
737        public long size() throws IOException {
738            if (origin instanceof FileInputStream) {
739                return ((FileInputStream) origin).getChannel().size();
740            }
741            throw unsupportedOperation("size");
742        }
743    }
744
745    /**
746     * An {@link IORandomAccessFile} origin.
747     *
748     * @since 2.18.0
749     */
750    public static class IORandomAccessFileOrigin extends AbstractRandomAccessFileOrigin<IORandomAccessFile, IORandomAccessFileOrigin> {
751
752        /**
753         * A {@link RandomAccessFile} origin.
754         *
755         * @param origin The origin, not null.
756         */
757        public IORandomAccessFileOrigin(final IORandomAccessFile origin) {
758            super(origin);
759        }
760
761        @SuppressWarnings("resource")
762        @Override
763        public File getFile() {
764            return get().getFile();
765        }
766
767        @Override
768        public Path getPath() {
769            return getFile().toPath();
770        }
771
772    }
773
774    /**
775     * An {@link OutputStream} origin.
776     * <p>
777     * This origin cannot provide some of the other aspects.
778     * </p>
779     */
780    public static class OutputStreamOrigin extends AbstractOrigin<OutputStream, OutputStreamOrigin> {
781
782        /**
783         * Constructs a new instance for the given origin.
784         *
785         * @param origin The origin, not null.
786         * @throws NullPointerException if {@code origin} is {@code null}.
787         */
788        public OutputStreamOrigin(final OutputStream origin) {
789            super(origin);
790        }
791
792        @Override
793        protected Channel getChannel(final OpenOption... options) throws IOException {
794            return Channels.newChannel(getOutputStream(options));
795        }
796
797        /**
798         * {@inheritDoc}
799         * <p>
800         * The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
801         * </p>
802         * <p>
803         * No conversion should occur when calling this method.
804         * </p>
805         */
806        @Override
807        public OutputStream getOutputStream(final OpenOption... options) {
808            // No conversion
809            return get();
810        }
811
812        /**
813         * {@inheritDoc}
814         * <p>
815         * The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
816         * </p>
817         */
818        @Override
819        public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
820            return new OutputStreamWriter(origin, Charsets.toCharset(charset));
821        }
822    }
823
824    /**
825     * A {@link Path} origin.
826     * <p>
827     * 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.
828     * </p>
829     */
830    public static class PathOrigin extends AbstractOrigin<Path, PathOrigin> {
831
832        /**
833         * Constructs a new instance for the given origin.
834         *
835         * @param origin The origin, not null.
836         * @throws NullPointerException if {@code origin} is {@code null}.
837         */
838        public PathOrigin(final Path origin) {
839            super(origin);
840        }
841
842        @Override
843        public byte[] getByteArray(final long position, final int length) throws IOException {
844            return RandomAccessFileMode.READ_ONLY.apply(origin, raf -> RandomAccessFiles.read(raf, position, length));
845        }
846
847        @Override
848        protected Channel getChannel(final OpenOption... options) throws IOException {
849            return Files.newByteChannel(getPath(), options);
850        }
851
852        @Override
853        public File getFile() {
854            return get().toFile();
855        }
856
857        /**
858         * {@inheritDoc}
859         *
860         * <p>
861         * No conversion should occur when calling this method.
862         * </p>
863         */
864        @Override
865        public Path getPath() {
866            // No conversion
867            return get();
868        }
869    }
870
871    /**
872     * A {@link RandomAccessFile} origin.
873     * <p>
874     * This origin cannot support File and Path since you cannot query a RandomAccessFile for those attributes; Use {@link IORandomAccessFileOrigin}
875     * instead.
876     * </p>
877     */
878    public static class RandomAccessFileOrigin extends AbstractRandomAccessFileOrigin<RandomAccessFile, RandomAccessFileOrigin> {
879
880        /**
881         * A {@link RandomAccessFile} origin.
882         * <p>
883         * Starting from this origin, you can everything except a Path and a File.
884         * </p>
885         *
886         * @param origin The origin, not null.
887         */
888        public RandomAccessFileOrigin(final RandomAccessFile origin) {
889            super(origin);
890        }
891
892    }
893
894    /**
895     * A {@link Reader} origin.
896     * <p>
897     * This origin cannot provide conversions to other aspects.
898     * </p>
899     */
900    public static class ReaderOrigin extends AbstractOrigin<Reader, ReaderOrigin> {
901
902        /**
903         * Constructs a new instance for the given origin.
904         *
905         * @param origin The origin, not null.
906         * @throws NullPointerException if {@code origin} is {@code null}.
907         */
908        public ReaderOrigin(final Reader origin) {
909            super(origin);
910        }
911
912        @Override
913        public byte[] getByteArray() throws IOException {
914            // TODO Pass in a Charset? Consider if call sites actually need this.
915            return IOUtils.toByteArray(origin, Charset.defaultCharset());
916        }
917
918        @Override
919        protected Channel getChannel(final OpenOption... options) throws IOException {
920            return Channels.newChannel(getInputStream());
921        }
922
923        /**
924         * {@inheritDoc}
925         * <p>
926         * The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
927         * </p>
928         */
929        @Override
930        public CharSequence getCharSequence(final Charset charset) throws IOException {
931            return IOUtils.toString(origin);
932        }
933
934        /**
935         * {@inheritDoc}
936         * <p>
937         * The {@code options} parameter is ignored since a {@link Reader} does not need an {@link OpenOption} to be read.
938         * </p>
939         */
940        @Override
941        public InputStream getInputStream(final OpenOption... options) throws IOException {
942            // TODO Pass in a Charset? Consider if call sites actually need this.
943            return ReaderInputStream.builder().setReader(origin).setCharset(Charset.defaultCharset()).get();
944        }
945
946        /**
947         * {@inheritDoc}
948         * <p>
949         * The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
950         * </p>
951         * <p>
952         * No conversion should occur when calling this method.
953         * </p>
954         */
955        @Override
956        public Reader getReader(final Charset charset) throws IOException {
957            // No conversion
958            return get();
959        }
960    }
961
962    /**
963     * A {@link URI} origin.
964     */
965    public static class URIOrigin extends AbstractOrigin<URI, URIOrigin> {
966
967        /**
968         * Options for connect and read from a URI.
969         *
970         * @since 2.22.0
971         */
972        public static final class URIOpenOption implements OpenOption {
973
974            /**
975             * Builds URIOpenOption.
976             */
977            public static class Builder extends AbstractSupplier<URIOpenOption, Builder> {
978
979                private Duration connectTimeout;
980                private Duration readTimeout;
981
982                /**
983                 * Constructs a new instance.
984                 */
985                public Builder() {
986                    // empty
987                }
988
989                @Override
990                public URIOpenOption get() {
991                    return new URIOpenOption(this);
992                }
993
994                /**
995                 * Sets the connect timeout duration.
996                 *
997                 * @param connectTimeout the connect timeout duration.
998                 * @return {@code this instance}.
999                 */
1000                public Builder setConnectTimeout(final Duration connectTimeout) {
1001                    this.connectTimeout = connectTimeout;
1002                    return asThis();
1003                }
1004
1005                /**
1006                 * Sets the read timeout duration.
1007                 *
1008                 * @param readTimeout the read timeout duration.
1009                 * @return {@code this instance}.
1010                 */
1011                public Builder setReadTimeout(final Duration readTimeout) {
1012                    this.readTimeout = readTimeout;
1013                    return asThis();
1014                }
1015            }
1016
1017            /**
1018             * Creates a new builder.
1019             *
1020             * @return a new builder.
1021             */
1022            public static Builder builder() {
1023                return new Builder();
1024            }
1025
1026            private final Duration connectTimeout;
1027
1028            private final Duration readTimeout;
1029
1030            private URIOpenOption(final Builder builder) {
1031                connectTimeout = builder.connectTimeout;
1032                readTimeout = builder.readTimeout;
1033            }
1034
1035            @Override
1036            public String toString() {
1037                return "URIOpenOption [connectTimeout=" + connectTimeout + ", readTimeout=" + readTimeout + "]";
1038            }
1039        }
1040
1041        private static final String SCHEME_HTTPS = "https";
1042        private static final String SCHEME_HTTP = "http";
1043
1044        /**
1045         * Constructs a new instance for the given origin.
1046         *
1047         * @param origin The origin, not null.
1048         * @throws NullPointerException if {@code origin} is {@code null}.
1049         */
1050        public URIOrigin(final URI origin) {
1051            super(origin);
1052        }
1053
1054        /**
1055         * {@inheritDoc}
1056         *
1057         * @see URIOpenOption
1058         */
1059        @Override
1060        protected Channel getChannel(final OpenOption... options) throws IOException {
1061            final URI uri = get();
1062            final String scheme = uri.getScheme();
1063            if (SCHEME_HTTP.equalsIgnoreCase(scheme) || SCHEME_HTTPS.equalsIgnoreCase(scheme)) {
1064                return Channels.newChannel(getInputStream(uri, options));
1065            }
1066            return Files.newByteChannel(getPath(), options);
1067        }
1068
1069        @Override
1070        public File getFile() {
1071            return getPath().toFile();
1072        }
1073
1074        /**
1075         * {@inheritDoc}
1076         * <p>
1077         * Set timeouts with a {@link URIOpenOption}.
1078         * </p>
1079         *
1080         * @see URIOpenOption
1081         * @see URLConnection#setConnectTimeout(int)
1082         * @see URLConnection#setReadTimeout(int)
1083         */
1084        @Override
1085        public InputStream getInputStream(final OpenOption... options) throws IOException {
1086            final URI uri = get();
1087            final String scheme = uri.getScheme();
1088            if (SCHEME_HTTP.equalsIgnoreCase(scheme) || SCHEME_HTTPS.equalsIgnoreCase(scheme)) {
1089                return getInputStream(uri, options);
1090            }
1091            return Files.newInputStream(getPath(), options);
1092        }
1093
1094        private InputStream getInputStream(final URI uri, final OpenOption... options) throws IOException {
1095            final URLConnection connection = uri.toURL().openConnection();
1096            if (options != null) {
1097                Stream.of(options).forEach(option -> {
1098                    if (option instanceof URIOpenOption) {
1099                        final URIOpenOption connOption = (URIOpenOption) option;
1100                        if (connOption.connectTimeout != null) {
1101                            connection.setConnectTimeout(toMillis(connOption.connectTimeout));
1102                        }
1103                        if (connOption.readTimeout != null) {
1104                            connection.setReadTimeout(toMillis(connOption.readTimeout));
1105                        }
1106                    }
1107                });
1108            }
1109            return connection.getInputStream();
1110        }
1111
1112        @Override
1113        public Path getPath() {
1114            return Paths.get(get());
1115        }
1116
1117        private int toMillis(final Duration duration) {
1118            return Math.toIntExact(duration.toMillis());
1119        }
1120    }
1121
1122    /**
1123     * A {@link Writer} origin.
1124     * <p>
1125     * This origin cannot provide conversions to other aspects.
1126     * </p>
1127     */
1128    public static class WriterOrigin extends AbstractOrigin<Writer, WriterOrigin> {
1129
1130        /**
1131         * Constructs a new instance for the given origin.
1132         *
1133         * @param origin The origin, not null.
1134         * @throws NullPointerException if {@code origin} is {@code null}.
1135         */
1136        public WriterOrigin(final Writer origin) {
1137            super(origin);
1138        }
1139
1140        @Override
1141        protected Channel getChannel(final OpenOption... options) throws IOException {
1142            return Channels.newChannel(getOutputStream());
1143        }
1144
1145        /**
1146         * {@inheritDoc}
1147         * <p>
1148         * The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
1149         * </p>
1150         */
1151        @Override
1152        public OutputStream getOutputStream(final OpenOption... options) throws IOException {
1153            // TODO Pass in a Charset? Consider if call sites actually need this.
1154            return WriterOutputStream.builder().setWriter(origin).setCharset(Charset.defaultCharset()).get();
1155        }
1156
1157        /**
1158         * {@inheritDoc}
1159         * <p>
1160         * The {@code charset} parameter is ignored since a {@link Writer} does not need a {@link Charset} to be written.
1161         * </p>
1162         * <p>
1163         * The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
1164         * </p>
1165         * <p>
1166         * No conversion should occur when calling this method.
1167         * </p>
1168         */
1169        @Override
1170        public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
1171            // No conversion
1172            return get();
1173        }
1174    }
1175
1176    /**
1177     * The non-null origin.
1178     */
1179    final T origin;
1180
1181    /**
1182     * Constructs a new instance for subclasses.
1183     *
1184     * @param origin The origin, not null.
1185     * @throws NullPointerException if {@code origin} is {@code null}.
1186     */
1187    protected AbstractOrigin(final T origin) {
1188        this.origin = Objects.requireNonNull(origin, "origin");
1189    }
1190
1191    /**
1192     * Gets the origin, never null.
1193     *
1194     * @return the origin, never null.
1195     */
1196    @Override
1197    public T get() {
1198        return origin;
1199    }
1200
1201    /**
1202     * Gets this origin as a byte array, if possible.
1203     *
1204     * @return this origin as a byte array, if possible.
1205     * @throws IOException                   if an I/O error occurs.
1206     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1207     */
1208    public byte[] getByteArray() throws IOException {
1209        return Files.readAllBytes(getPath());
1210    }
1211
1212    /**
1213     * Gets a portion of this origin as a byte array, if possible.
1214     *
1215     * @param position the initial index of the range to be copied, inclusive.
1216     * @param length   How many bytes to copy.
1217     * @return this origin as a byte array, if possible.
1218     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1219     * @throws ArithmeticException           if the {@code position} overflows an int.
1220     * @throws IOException                   if an I/O error occurs.
1221     * @since 2.13.0
1222     */
1223    public byte[] getByteArray(final long position, final int length) throws IOException {
1224        final byte[] bytes = getByteArray();
1225        // Checks for int overflow.
1226        final int start = Math.toIntExact(position);
1227        if (start < 0 || length < 0 || start + length < 0 || start + length > bytes.length) {
1228            throw new IllegalArgumentException("Couldn't read array (start: " + start + ", length: " + length + ", data length: " + bytes.length + ").");
1229        }
1230        return Arrays.copyOfRange(bytes, start, start + length);
1231    }
1232
1233    /**
1234     * Gets this origin as a Channel of the given type, if possible.
1235     *
1236     * @param channelType The type of channel to return.
1237     * @param options Options specifying how a file-based origin is opened, ignored otherwise.
1238     * @return A new Channel on the origin of the given type.
1239     * @param <C> The type of channel to return.
1240     * @throws IOException                   If an I/O error occurs.
1241     * @throws UnsupportedOperationException If this origin cannot be converted to a channel of the given type.
1242     * @see #getChannel(OpenOption...)
1243     * @since 2.21.0
1244     */
1245    public final <C extends Channel> C getChannel(final Class<C> channelType, final OpenOption... options) throws IOException {
1246        Objects.requireNonNull(channelType, "channelType");
1247        final Channel channel = getChannel(options);
1248        if (channelType.isInstance(channel)) {
1249            return channelType.cast(channel);
1250        }
1251        throw unsupportedChannelType(channelType);
1252    }
1253
1254    /**
1255     * Gets this origin as a Channel, if possible.
1256     *
1257     * @param options Options specifying how a file-based origin is opened, ignored otherwise.
1258     * @return A new Channel on the origin.
1259     * @throws IOException                   If an I/O error occurs.
1260     * @throws UnsupportedOperationException If this origin cannot be converted to a channel.
1261     * @see #getChannel(Class, OpenOption...)
1262     * @since 2.21.0
1263     */
1264    protected Channel getChannel(final OpenOption... options) throws IOException {
1265        throw unsupportedOperation("getChannel");
1266    }
1267
1268    /**
1269     * Gets this origin as a byte array, if possible.
1270     *
1271     * @param charset The charset to use if conversion from bytes is needed.
1272     * @return this origin as a byte array, if possible.
1273     * @throws IOException                   if an I/O error occurs.
1274     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1275     */
1276    public CharSequence getCharSequence(final Charset charset) throws IOException {
1277        return new String(getByteArray(), charset);
1278    }
1279
1280    /**
1281     * Gets this origin as a File, if possible.
1282     *
1283     * @return this origin as a File, if possible.
1284     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
1285     */
1286    public File getFile() {
1287        throw unsupportedOperation("getFile");
1288    }
1289
1290    /**
1291     * Gets this origin as an InputStream, if possible.
1292     *
1293     * @param options options specifying how the file is opened.
1294     * @return this origin as an InputStream, if possible.
1295     * @throws IOException                   if an I/O error occurs.
1296     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1297     */
1298    public InputStream getInputStream(final OpenOption... options) throws IOException {
1299        return Files.newInputStream(getPath(), options);
1300    }
1301
1302    /**
1303     * Gets this origin as an OutputStream, if possible.
1304     *
1305     * @param options options specifying how the file is opened.
1306     * @return this origin as an OutputStream, if possible.
1307     * @throws IOException                   if an I/O error occurs.
1308     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1309     */
1310    public OutputStream getOutputStream(final OpenOption... options) throws IOException {
1311        return Files.newOutputStream(getPath(), options);
1312    }
1313
1314    /**
1315     * Gets this origin as a Path, if possible.
1316     *
1317     * @return this origin as a Path, if possible.
1318     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
1319     */
1320    public Path getPath() {
1321        throw unsupportedOperation("getPath");
1322    }
1323
1324    /**
1325     * Gets this origin as a RandomAccessFile, if possible.
1326     *
1327     * @param openOption options like {@link StandardOpenOption}.
1328     * @return this origin as a RandomAccessFile, if possible.
1329     * @throws FileNotFoundException         See {@link RandomAccessFile#RandomAccessFile(File, String)}.
1330     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
1331     * @since 2.18.0
1332     */
1333    public RandomAccessFile getRandomAccessFile(final OpenOption... openOption) throws FileNotFoundException {
1334        return RandomAccessFileMode.valueOf(openOption).create(getFile());
1335    }
1336
1337    /**
1338     * Gets a new Reader on the origin, buffered by default.
1339     *
1340     * @param charset the charset to use for decoding, null maps to the default Charset.
1341     * @return a new Reader on the origin.
1342     * @throws IOException if an I/O error occurs opening the file.
1343     */
1344    public Reader getReader(final Charset charset) throws IOException {
1345        return Files.newBufferedReader(getPath(), Charsets.toCharset(charset));
1346    }
1347
1348    /**
1349     * Gets simple name of the underlying class.
1350     *
1351     * @return The simple name of the underlying class.
1352     */
1353    private String getSimpleClassName() {
1354        return getClass().getSimpleName();
1355    }
1356
1357    /**
1358     * Gets a new Writer on the origin, buffered by default.
1359     *
1360     * @param charset the charset to use for encoding.
1361     * @param options options specifying how the file is opened.
1362     * @return a new Writer on the origin.
1363     * @throws IOException                   if an I/O error occurs opening or creating the file.
1364     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
1365     */
1366    public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
1367        return Files.newBufferedWriter(getPath(), Charsets.toCharset(charset), options);
1368    }
1369
1370    /**
1371     * Gets the size of the origin, if possible.
1372     *
1373     * @return the size of the origin in bytes or characters.
1374     * @throws IOException if an I/O error occurs.
1375     * @since 2.13.0
1376     */
1377    public long size() throws IOException {
1378        return Files.size(getPath());
1379    }
1380
1381    @Override
1382    public String toString() {
1383        return getSimpleClassName() + "[" + origin.toString() + "]";
1384    }
1385
1386    UnsupportedOperationException unsupportedChannelType(final Class<? extends Channel> channelType) {
1387        return new UnsupportedOperationException(String.format(
1388                "%s#getChannel(%s) for %s origin %s",
1389                getSimpleClassName(),
1390                channelType.getSimpleName(),
1391                origin.getClass().getSimpleName(),
1392                origin));
1393    }
1394
1395    UnsupportedOperationException unsupportedOperation(final String method) {
1396        return new UnsupportedOperationException(String.format(
1397                "%s#%s() for %s origin %s",
1398                getSimpleClassName(), method, origin.getClass().getSimpleName(), origin));
1399    }
1400}