com.signalfx.shaded.apache.commons.io.build.AbstractOrigin Maven / Gradle / Ivy
Show all versions of signalfx-java Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.signalfx.shaded.apache.commons.io.build;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.spi.FileSystemProvider;
import java.util.Arrays;
import java.util.Objects;
import com.signalfx.shaded.apache.commons.io.IOUtils;
import com.signalfx.shaded.apache.commons.io.RandomAccessFileMode;
import com.signalfx.shaded.apache.commons.io.RandomAccessFiles;
import com.signalfx.shaded.apache.commons.io.file.spi.FileSystemProviders;
import com.signalfx.shaded.apache.commons.io.input.CharSequenceInputStream;
import com.signalfx.shaded.apache.commons.io.input.CharSequenceReader;
import com.signalfx.shaded.apache.commons.io.input.ReaderInputStream;
import com.signalfx.shaded.apache.commons.io.output.WriterOutputStream;
/**
* Abstracts the origin of data for builders like a {@link File}, {@link Path}, {@link Reader}, {@link Writer}, {@link InputStream}, {@link OutputStream}, and
* {@link URI}.
*
* Some methods may throw {@link UnsupportedOperationException} if that method is not implemented in a concrete subclass, see {@link #getFile()} and
* {@link #getPath()}.
*
*
* @param the type of instances to build.
* @param the type of builder subclass.
* @since 2.12.0
*/
public abstract class AbstractOrigin> extends AbstractSupplier {
/**
* A {@code byte[]} origin.
*/
public static class ByteArrayOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public ByteArrayOrigin(final byte[] origin) {
super(origin);
}
@Override
public byte[] getByteArray() {
// No conversion
return get();
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@code byte[]} does not need an {@link OpenOption} to be read.
*
*/
@Override
public InputStream getInputStream(final OpenOption... options) throws IOException {
return new ByteArrayInputStream(origin);
}
@Override
public Reader getReader(final Charset charset) throws IOException {
return new InputStreamReader(getInputStream(), charset);
}
@Override
public long size() throws IOException {
return origin.length;
}
}
/**
* A {@link CharSequence} origin.
*/
public static class CharSequenceOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public CharSequenceOrigin(final CharSequence origin) {
super(origin);
}
@Override
public byte[] getByteArray() {
// TODO Pass in a Charset? Consider if call sites actually need this.
return origin.toString().getBytes(Charset.defaultCharset());
}
/**
* {@inheritDoc}
*
* The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
*
*/
@Override
public CharSequence getCharSequence(final Charset charset) {
// No conversion
return get();
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link CharSequence} does not need an {@link OpenOption} to be read.
*
*/
@Override
public InputStream getInputStream(final OpenOption... options) throws IOException {
// TODO Pass in a Charset? Consider if call sites actually need this.
return CharSequenceInputStream.builder().setCharSequence(getCharSequence(Charset.defaultCharset())).get();
}
/**
* {@inheritDoc}
*
* The {@code charset} parameter is ignored since a {@link CharSequence} does not need a {@link Charset} to be read.
*
*/
@Override
public Reader getReader(final Charset charset) throws IOException {
return new CharSequenceReader(get());
}
@Override
public long size() throws IOException {
return origin.length();
}
}
/**
* A {@link File} origin.
*
* 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.
*
*/
public static class FileOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public FileOrigin(final File origin) {
super(origin);
}
@Override
public byte[] getByteArray(final long position, final int length) throws IOException {
try (RandomAccessFile raf = RandomAccessFileMode.READ_ONLY.create(origin)) {
return RandomAccessFiles.read(raf, position, length);
}
}
@Override
public File getFile() {
// No conversion
return get();
}
@Override
public Path getPath() {
return get().toPath();
}
}
/**
* An {@link InputStream} origin.
*
* This origin cannot provide some of the other aspects.
*
*/
public static class InputStreamOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public InputStreamOrigin(final InputStream origin) {
super(origin);
}
@Override
public byte[] getByteArray() throws IOException {
return IOUtils.toByteArray(origin);
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link InputStream} does not need an {@link OpenOption} to be read.
*
*/
@Override
public InputStream getInputStream(final OpenOption... options) {
// No conversion
return get();
}
@Override
public Reader getReader(final Charset charset) throws IOException {
return new InputStreamReader(getInputStream(), charset);
}
}
/**
* An {@link OutputStream} origin.
*
* This origin cannot provide some of the other aspects.
*
*/
public static class OutputStreamOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public OutputStreamOrigin(final OutputStream origin) {
super(origin);
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
*
*/
@Override
public OutputStream getOutputStream(final OpenOption... options) {
// No conversion
return get();
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link OutputStream} does not need an {@link OpenOption} to be written.
*
*/
@Override
public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
return new OutputStreamWriter(origin, charset);
}
}
/**
* A {@link Path} origin.
*
* 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.
*
*/
public static class PathOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public PathOrigin(final Path origin) {
super(origin);
}
@Override
public byte[] getByteArray(final long position, final int length) throws IOException {
try (RandomAccessFile raf = RandomAccessFileMode.READ_ONLY.create(origin)) {
return RandomAccessFiles.read(raf, position, length);
}
}
@Override
public File getFile() {
return get().toFile();
}
@Override
public Path getPath() {
// No conversion
return get();
}
}
/**
* An {@link Reader} origin.
*
* This origin cannot provide other aspects.
*
*/
public static class ReaderOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public ReaderOrigin(final Reader origin) {
super(origin);
}
@Override
public byte[] getByteArray() throws IOException {
// TODO Pass in a Charset? Consider if call sites actually need this.
return IOUtils.toByteArray(origin, Charset.defaultCharset());
}
/**
* {@inheritDoc}
*
* The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
*
*/
@Override
public CharSequence getCharSequence(final Charset charset) throws IOException {
return IOUtils.toString(origin);
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link Reader} does not need an {@link OpenOption} to be read.
*
*/
@Override
public InputStream getInputStream(final OpenOption... options) throws IOException {
// TODO Pass in a Charset? Consider if call sites actually need this.
return ReaderInputStream.builder().setReader(origin).setCharset(Charset.defaultCharset()).get();
}
/**
* {@inheritDoc}
*
* The {@code charset} parameter is ignored since a {@link Reader} does not need a {@link Charset} to be read.
*
*/
@Override
public Reader getReader(final Charset charset) throws IOException {
// No conversion
return get();
}
}
/**
* A {@link URI} origin.
*/
public static class URIOrigin extends AbstractOrigin {
private static final String SCHEME_HTTPS = "https";
private static final String SCHEME_HTTP = "http";
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public URIOrigin(final URI origin) {
super(origin);
}
@Override
public File getFile() {
return getPath().toFile();
}
@Override
public InputStream getInputStream(final OpenOption... options) throws IOException {
final URI uri = get();
final String scheme = uri.getScheme();
final FileSystemProvider fileSystemProvider = FileSystemProviders.installed().getFileSystemProvider(scheme);
if (fileSystemProvider != null) {
return Files.newInputStream(fileSystemProvider.getPath(uri), options);
}
if (SCHEME_HTTP.equalsIgnoreCase(scheme) || SCHEME_HTTPS.equalsIgnoreCase(scheme)) {
return uri.toURL().openStream();
}
return Files.newInputStream(getPath(), options);
}
@Override
public Path getPath() {
return Paths.get(get());
}
}
/**
* An {@link Writer} origin.
*
* This origin cannot provide other aspects.
*
*/
public static class WriterOrigin extends AbstractOrigin {
/**
* Constructs a new instance for the given origin.
*
* @param origin The origin.
*/
public WriterOrigin(final Writer origin) {
super(origin);
}
/**
* {@inheritDoc}
*
* The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
*
*/
@Override
public OutputStream getOutputStream(final OpenOption... options) throws IOException {
// TODO Pass in a Charset? Consider if call sites actually need this.
return WriterOutputStream.builder().setWriter(origin).setCharset(Charset.defaultCharset()).get();
}
/**
* {@inheritDoc}
*
* The {@code charset} parameter is ignored since a {@link Writer} does not need a {@link Charset} to be written.
*
*
* The {@code options} parameter is ignored since a {@link Writer} does not need an {@link OpenOption} to be written.
*
*/
@Override
public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
// No conversion
return get();
}
}
/**
* The non-null origin.
*/
final T origin;
/**
* Constructs a new instance for a subclass.
*
* @param origin The origin.
*/
protected AbstractOrigin(final T origin) {
this.origin = Objects.requireNonNull(origin, "origin");
}
/**
* Gets the origin.
*
* @return the origin.
*/
@Override
public T get() {
return origin;
}
/**
* Gets this origin as a byte array, if possible.
*
* @return this origin as a byte array, if possible.
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
*/
public byte[] getByteArray() throws IOException {
return Files.readAllBytes(getPath());
}
/**
* Gets this origin as a byte array, if possible.
*
* @param position the initial index of the range to be copied, inclusive.
* @param length How many bytes to copy.
* @return this origin as a byte array, if possible.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
* @throws ArithmeticException if the {@code position} overflows an int
* @throws IOException if an I/O error occurs.
* @since 2.13.0
*/
public byte[] getByteArray(final long position, final int length) throws IOException {
final byte[] bytes = getByteArray();
// Checks for int overflow.
final int start = Math.toIntExact(position);
if (start < 0 || length < 0 || start + length < 0 || start + length > bytes.length) {
throw new IllegalArgumentException("Couldn't read array (start: " + start + ", length: " + length + ", data length: " + bytes.length + ").");
}
return Arrays.copyOfRange(bytes, start, start + length);
}
/**
* Gets this origin as a byte array, if possible.
*
* @param charset The charset to use if conversion from bytes is needed.
* @return this origin as a byte array, if possible.
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
*/
public CharSequence getCharSequence(final Charset charset) throws IOException {
return new String(getByteArray(), charset);
}
/**
* Gets this origin as a Path, if possible.
*
* @return this origin as a Path, if possible.
* @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
*/
public File getFile() {
throw new UnsupportedOperationException(
String.format("%s#getFile() for %s origin %s", getSimpleClassName(), origin.getClass().getSimpleName(), origin));
}
/**
* Gets this origin as an InputStream, if possible.
*
* @param options options specifying how the file is opened
* @return this origin as an InputStream, if possible.
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
*/
public InputStream getInputStream(final OpenOption... options) throws IOException {
return Files.newInputStream(getPath(), options);
}
/**
* Gets this origin as an OutputStream, if possible.
*
* @param options options specifying how the file is opened
* @return this origin as an OutputStream, if possible.
* @throws IOException if an I/O error occurs.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
*/
public OutputStream getOutputStream(final OpenOption... options) throws IOException {
return Files.newOutputStream(getPath(), options);
}
/**
* Gets this origin as a Path, if possible.
*
* @return this origin as a Path, if possible.
* @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
*/
public Path getPath() {
throw new UnsupportedOperationException(
String.format("%s#getPath() for %s origin %s", getSimpleClassName(), origin.getClass().getSimpleName(), origin));
}
/**
* Gets a new Reader on the origin, buffered by default.
*
* @param charset the charset to use for decoding
* @return a new Reader on the origin.
* @throws IOException if an I/O error occurs opening the file.
*/
public Reader getReader(final Charset charset) throws IOException {
return Files.newBufferedReader(getPath(), charset);
}
private String getSimpleClassName() {
return getClass().getSimpleName();
}
/**
* Gets a new Writer on the origin, buffered by default.
*
* @param charset the charset to use for encoding
* @param options options specifying how the file is opened
* @return a new Writer on the origin.
* @throws IOException if an I/O error occurs opening or creating the file.
* @throws UnsupportedOperationException if the origin cannot be converted to a Path.
*/
public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
return Files.newBufferedWriter(getPath(), charset, options);
}
/**
* Gets the size of the origin, if possible.
*
* @return the size of the origin in bytes or characters.
* @throws IOException if an I/O error occurs.
* @since 2.13.0
*/
public long size() throws IOException {
return Files.size(getPath());
}
@Override
public String toString() {
return getSimpleClassName() + "[" + origin.toString() + "]";
}
}