Skip to content

Commit

Permalink
Add resource utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Jun 30, 2023
1 parent 1499ac6 commit 5b6ef1c
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 63 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

- Add lock factory to `File*` utilities
- Add uncloseable stream utilities
- Add resource utilities

### Fixed

Expand Down
6 changes: 0 additions & 6 deletions java-io-base/src/main/java/internal/io/text/LegacyFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package internal.io.text;

import lombok.NonNull;
import nbbrd.io.Resource;
import nbbrd.io.function.IOSupplier;

import java.io.*;
Expand All @@ -33,11 +32,6 @@
@lombok.experimental.UtilityClass
public class LegacyFiles {

public static InputStream openResource(Class<?> anchor, String name) throws IOException {
return Resource.getResourceAsStream(anchor, name)
.orElseThrow(() -> new IOException("Missing resource '" + name + "' of '" + anchor.getName() + "'"));
}

public static Reader openReader(CharSequence source) {
return new StringReader(source.toString());
}
Expand Down
2 changes: 1 addition & 1 deletion java-io-base/src/main/java/nbbrd/io/FileParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface FileParser<T> {
}

default @NonNull T parseResource(@NonNull Class<?> type, @NonNull String name) throws IOException {
try (InputStream resource = LegacyFiles.openResource(type, name)) {
try (InputStream resource = Resource.newInputStream(type, name)) {
return parseStream(resource);
}
}
Expand Down
15 changes: 13 additions & 2 deletions java-io-base/src/main/java/nbbrd/io/Resource.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,22 @@ public Optional<File> getFile(@NonNull Path path) {
}
}

@NonNull
public Optional<InputStream> getResourceAsStream(@NonNull Class<?> anchor, @NonNull String name) {
/**
* @deprecated use {@link #newInputStream(Class, String)} instead.
*/
@Deprecated
public @NonNull Optional<InputStream> getResourceAsStream(@NonNull Class<?> anchor, @NonNull String name) {
return Optional.ofNullable(anchor.getResourceAsStream(name));
}

public static @NonNull InputStream newInputStream(@NonNull Class<?> anchor, @NonNull String name) throws IOException {
InputStream result = anchor.getResourceAsStream(name);
if (result == null) {
throw new IOException("Missing resource '" + name + "' of '" + anchor.getName() + "'");
}
return result;
}

@SuppressWarnings("ThrowableResultIgnored")
public void ensureClosed(@NonNull Throwable exception, @Nullable Closeable closeable) {
if (closeable != null) {
Expand Down
2 changes: 1 addition & 1 deletion java-io-base/src/main/java/nbbrd/io/text/TextParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public interface TextParser<T> {
}

default @NonNull T parseResource(@NonNull Class<?> type, @NonNull String name, @NonNull Charset encoding) throws IOException {
try (InputStream resource = LegacyFiles.openResource(type, name)) {
try (InputStream resource = Resource.newInputStream(type, name)) {
return parseStream(resource, encoding);
}
}
Expand Down
10 changes: 10 additions & 0 deletions java-io-base/src/main/java/nbbrd/io/text/TextResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@
import java.nio.charset.CharsetEncoder;
import java.util.Optional;

import static nbbrd.io.Resource.newInputStream;

@lombok.experimental.UtilityClass
public class TextResource {

/**
* @deprecated use {@link #newBufferedReader(Class, String, CharsetDecoder)} instead.
*/
@Deprecated
public @NonNull Optional<BufferedReader> getResourceAsBufferedReader(@NonNull Class<?> anchor, @NonNull String name, @NonNull Charset charset) {
return Resource.getResourceAsStream(anchor, name)
.map(stream -> newBufferedReader(stream, charset.newDecoder()));
}

public @NonNull BufferedReader newBufferedReader(@NonNull Class<?> anchor, @NonNull String name, @NonNull CharsetDecoder decoder) throws IOException {
return newBufferedReader(newInputStream(anchor, name), decoder);
}

public @NonNull BufferedReader newBufferedReader(@NonNull InputStream stream, @NonNull CharsetDecoder decoder) {
return new BufferedReader(new InputStreamReader(stream, decoder));
}
Expand Down
9 changes: 5 additions & 4 deletions java-io-base/src/test/java/_test/io/ResourceId.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

import internal.io.text.InternalTextResource;
import lombok.NonNull;
import nbbrd.io.Resource;
import nbbrd.io.text.TextResource;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

import static nbbrd.io.Resource.newInputStream;
import static nbbrd.io.text.TextResource.newBufferedReader;

@lombok.Value
public class ResourceId {

Expand All @@ -21,7 +22,7 @@ public class ResourceId {
String name;

public InputStream open() throws IOException {
return Resource.getResourceAsStream(anchor, name).orElseThrow(IOException::new);
return newInputStream(anchor, name);
}

public Path copyTo(Path temp) throws IOException {
Expand All @@ -45,7 +46,7 @@ public byte[] toBytes() throws IOException {
}

public BufferedReader open(Charset encoding) throws IOException {
return TextResource.getResourceAsBufferedReader(anchor, name, encoding).orElseThrow(IOException::new);
return newBufferedReader(anchor, name, encoding.newDecoder());
}

public String copyToString(Charset encoding) throws IOException {
Expand Down
33 changes: 26 additions & 7 deletions java-io-base/src/test/java/nbbrd/io/ResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static nbbrd.io.Resource.getResourceAsStream;
import static nbbrd.io.Resource.newInputStream;
import static org.assertj.core.api.Assertions.*;

/**
Expand All @@ -46,7 +48,7 @@
public class ResourceTest {

@Test
@SuppressWarnings("null")
@SuppressWarnings({"null", "DataFlowIssue"})
public void testGetFile(@TempDir Path temp) throws IOException {
assertThatNullPointerException().isThrownBy(() -> Resource.getFile(null));

Expand All @@ -68,19 +70,35 @@ public void testGetFile(@TempDir Path temp) throws IOException {
}

@Test
@SuppressWarnings("null")
@SuppressWarnings({"null", "DataFlowIssue", "deprecation"})
public void testGetResourceAsStream() throws IOException {
assertThatNullPointerException().isThrownBy(() -> Resource.getResourceAsStream(null, ""));
assertThatNullPointerException().isThrownBy(() -> Resource.getResourceAsStream(ResourceTest.class, null));
assertThatNullPointerException().isThrownBy(() -> getResourceAsStream(null, ""));
assertThatNullPointerException().isThrownBy(() -> getResourceAsStream(ResourceTest.class, null));

assertThat(Resource.getResourceAsStream(ResourceTest.class, "hello")).isEmpty();
try (InputStream stream = Resource.getResourceAsStream(ResourceTest.class, "/nbbrd/io/zip/test.zip").get()) {
assertThat(getResourceAsStream(ResourceTest.class, "hello")).isEmpty();
try (InputStream stream = getResourceAsStream(ResourceTest.class, "/nbbrd/io/zip/test.zip").orElseThrow(IOException::new)) {
assertThat(stream).isNotNull();
}
}

@Test
@SuppressWarnings("null")
@SuppressWarnings({"null", "resource", "DataFlowIssue"})
public void testNewInputStream() throws IOException {
assertThatNullPointerException().isThrownBy(() -> newInputStream(null, ""));
assertThatNullPointerException().isThrownBy(() -> newInputStream(ResourceTest.class, null));

assertThatIOException()
.isThrownBy(() -> newInputStream(ResourceTest.class, "missing_resource"))
.withMessageContaining("missing_resource")
.withMessageContaining(ResourceTest.class.getName());

try (InputStream stream = newInputStream(ResourceTest.class, "/nbbrd/io/zip/test.zip")) {
assertThat(stream).isNotEmpty();
}
}

@Test
@SuppressWarnings({"null", "DataFlowIssue"})
public void testEnsureClosed() throws IOException {
assertThatNullPointerException().isThrownBy(() -> Resource.ensureClosed(null, IORunnable.noOp().asCloseable()));

Expand Down Expand Up @@ -139,6 +157,7 @@ public void testCloseBoth() throws IOException {
.doesNotThrowAnyException();
}

@SuppressWarnings("DataFlowIssue")
@Test
public void testProcess() throws IOException, URISyntaxException {
URL url = ResourceTest.class.getResource("/nbbrd/io/zip/test.zip");
Expand Down
29 changes: 25 additions & 4 deletions java-io-base/src/test/java/nbbrd/io/text/TextResourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,50 @@
import org.junit.jupiter.api.Test;

import java.io.*;
import java.nio.charset.CharsetDecoder;

import static java.nio.charset.StandardCharsets.UTF_8;
import static nbbrd.io.text.TextResource.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.assertj.core.api.Assertions.*;

/**
* @author Philippe Charles
*/
public class TextResourceTest {

@Test
@SuppressWarnings("null")
@SuppressWarnings({"null", "DataFlowIssue", "deprecation"})
public void testGetResourceAsBufferedReader() throws IOException {
assertThatNullPointerException().isThrownBy(() -> getResourceAsBufferedReader(null, "", UTF_8));
assertThatNullPointerException().isThrownBy(() -> getResourceAsBufferedReader(TextResourceTest.class, null, UTF_8));
assertThatNullPointerException().isThrownBy(() -> getResourceAsBufferedReader(TextResourceTest.class, "", null));

assertThat(getResourceAsBufferedReader(TextResourceTest.class, "missing", UTF_8)).isEmpty();
try (BufferedReader reader = getResourceAsBufferedReader(TextResourceTest.class, "/nbbrd/io/text/hello.txt", UTF_8).get()) {
try (BufferedReader reader = getResourceAsBufferedReader(TextResourceTest.class, "/nbbrd/io/text/hello.txt", UTF_8).orElseThrow(IOException::new)) {
assertThat(reader.lines()).contains("world");
}
}

@Test
@SuppressWarnings({"null", "resource", "DataFlowIssue"})
public void testNewBufferedReaderOfResource() throws IOException {
CharsetDecoder utf8 = UTF_8.newDecoder();

assertThatNullPointerException().isThrownBy(() -> newBufferedReader(null, "", utf8));
assertThatNullPointerException().isThrownBy(() -> newBufferedReader(TextResourceTest.class, null, utf8));
assertThatNullPointerException().isThrownBy(() -> newBufferedReader(TextResourceTest.class, "", null));

assertThatIOException()
.isThrownBy(() -> newBufferedReader(TextResourceTest.class, "missing", utf8))
.withMessageContaining("missing")
.withMessageContaining(TextResourceTest.class.getName());

try (BufferedReader reader = newBufferedReader(TextResourceTest.class, "/nbbrd/io/text/hello.txt", utf8)) {
assertThat(reader.lines()).contains("world");
}
}

@SuppressWarnings({"resource", "DataFlowIssue"})
@Test
public void testNewBufferedReader() throws IOException {
byte[] bytes = "world".getBytes(UTF_8);
Expand All @@ -55,6 +75,7 @@ public void testNewBufferedReader() throws IOException {
}
}

@SuppressWarnings({"resource", "DataFlowIssue"})
@Test
public void testNewBufferedWriter() throws IOException {
assertThatNullPointerException().isThrownBy(() -> newBufferedWriter(null, UTF_8.newEncoder()));
Expand Down
9 changes: 5 additions & 4 deletions java-io-base/src/test/java/nbbrd/io/zip/ZipTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.nio.file.StandardCopyOption;

import static java.nio.charset.StandardCharsets.UTF_8;
import static nbbrd.io.Resource.newInputStream;
import static org.assertj.core.api.Assertions.*;

/**
Expand All @@ -43,7 +44,7 @@ public class ZipTest {
@BeforeAll
public static void beforeClass(@TempDir Path temp) throws IOException {
FILE = Files.createFile(temp.resolve("test.zip")).toFile();
try (InputStream stream = Resource.getResourceAsStream(ZipTest.class, "test.zip").get()) {
try (InputStream stream = newInputStream(ZipTest.class, "test.zip")) {
Files.copy(stream, FILE.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
Expand Down Expand Up @@ -77,7 +78,7 @@ public void testZipLoaderCopyOf() throws IOException {
assertThatNullPointerException().isThrownBy(() -> Zip.loaderCopyOf(null, IOPredicate.of(true)));
assertThatNullPointerException().isThrownBy(() -> Zip.loaderCopyOf(ZipTest.class.getResourceAsStream(""), null));

try (InputStream file = Resource.getResourceAsStream(ZipTest.class, "test.zip").get()) {
try (InputStream file = newInputStream(ZipTest.class, "test.zip")) {
try (Resource.Loader<String> loader = Zip.loaderCopyOf(file, IOPredicate.of(true))) {
assertThatNullPointerException().isThrownBy(() -> loader.load(null));
assertThatIOException().isThrownBy(() -> loader.load("xyz"));
Expand All @@ -90,7 +91,7 @@ public void testZipLoaderCopyOf() throws IOException {
}
}

try (InputStream file = Resource.getResourceAsStream(ZipTest.class, "test.zip").get()) {
try (InputStream file = newInputStream(ZipTest.class, "test.zip")) {
try (Resource.Loader<String> loader = Zip.loaderCopyOf(file, o -> o.getName().startsWith("folder1"))) {
assertThatNullPointerException().isThrownBy(() -> loader.load(null));
assertThatIOException().isThrownBy(() -> loader.load("xyz"));
Expand All @@ -101,7 +102,7 @@ public void testZipLoaderCopyOf() throws IOException {
}
}

try (InputStream file = Resource.getResourceAsStream(ZipTest.class, "test.zip").get()) {
try (InputStream file = newInputStream(ZipTest.class, "test.zip")) {
assertThatIllegalStateException().isThrownBy(() -> {
Resource.Loader<String> loader = Zip.loaderCopyOf(file, IOPredicate.of(true));
loader.close();
Expand Down
51 changes: 25 additions & 26 deletions java-io-curl/src/test/java/nbbrd/io/curl/CurlTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package nbbrd.io.curl;

import nbbrd.io.Resource;
import nbbrd.io.sys.ProcessReader;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
Expand All @@ -17,7 +18,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static java.util.Collections.*;
import static nbbrd.io.text.TextResource.getResourceAsBufferedReader;
import static nbbrd.io.text.TextResource.newBufferedReader;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.atIndex;

Expand Down Expand Up @@ -108,32 +109,30 @@ public void testCommandBuilder(@TempDir File temp) throws MalformedURLException

@Test
public void testHead() throws IOException {
try (InputStream stream = Resource.getResourceAsStream(CurlTest.class, "curlhead.txt").orElseThrow(IOException::new)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8))) {
assertThat(Curl.Head.parseResponse(reader))
.singleElement()
.isEqualTo(new Curl.Head(
new Curl.Status(200, "OK"),
new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER) {
{
put("Date", singletonList("Wed, 20 Oct 2021 10:58:37 GMT"));
put("Expires", singletonList("-1"));
put("Cache-Control", singletonList("private, max-age=0"));
put("Content-Type", singletonList("text/html; charset=ISO-8859-1"));
put("P3P", singletonList("CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\""));
put("Server", singletonList("gws"));
put("X-XSS-Protection", singletonList("0"));
put("X-Frame-Options", singletonList("SAMEORIGIN"));
put("Accept-Ranges", singletonList("none"));
put("Vary", singletonList("Accept-Encoding"));
put("Transfer-Encoding", singletonList("chunked"));
}
try (BufferedReader reader = newBufferedReader(CurlTest.class, "curlhead.txt", UTF_8.newDecoder())) {
assertThat(Curl.Head.parseResponse(reader))
.singleElement()
.isEqualTo(new Curl.Head(
new Curl.Status(200, "OK"),
new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER) {
{
put("Date", singletonList("Wed, 20 Oct 2021 10:58:37 GMT"));
put("Expires", singletonList("-1"));
put("Cache-Control", singletonList("private, max-age=0"));
put("Content-Type", singletonList("text/html; charset=ISO-8859-1"));
put("P3P", singletonList("CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\""));
put("Server", singletonList("gws"));
put("X-XSS-Protection", singletonList("0"));
put("X-Frame-Options", singletonList("SAMEORIGIN"));
put("Accept-Ranges", singletonList("none"));
put("Vary", singletonList("Accept-Encoding"));
put("Transfer-Encoding", singletonList("chunked"));
}
));
}
}
));
}

try (BufferedReader reader = getResourceAsBufferedReader(CurlTest.class, "curlhead2.txt", UTF_8).orElseThrow(IOException::new)) {
try (BufferedReader reader = newBufferedReader(CurlTest.class, "curlhead2.txt", UTF_8.newDecoder())) {
assertThat(Curl.Head.parseResponse(reader))
.hasSize(2)
.satisfies(head -> assertThat(head.getStatus().getCode()).isEqualTo(301), atIndex(0))
Expand Down
Loading

0 comments on commit 5b6ef1c

Please sign in to comment.