33import java .io .IOException ;
44import java .io .InputStream ;
55import java .io .UncheckedIOException ;
6+ import java .net .URL ;
67import java .util .concurrent .Callable ;
78
89/**
910 * FIXME: We're currently missing caching of the resource's data // Hugi 2024-06-22
10- * FIXME: NGResource should probably be an interface, since we need to accommodate JAR resources, filesystem resources, dynamic resources etc. etc.
11+ * FIXME: We need to accommodate JAR resources, filesystem resources, dynamic resources etc. etc. // Hugi 2025-08-03
12+ * FIXME: A resource should know it's name, length and possibly content type (although the default implementation may derive that from it's name)
1113 */
1214
13- public class NGResource {
15+ public interface NGResource {
1416
1517 /**
16- * A method that will provide us with the actual inputStream
18+ * @return A stream providing the resource data. The stream is the consumer's responsibility to close.
1719 */
18- private Callable <InputStream > _inputStreamSupplier ;
19-
20- /**
21- * @return A resource that obtains it's data from the given supplier
22- */
23- public static NGResource of ( final Callable <InputStream > inputStreamSupplier ) {
24- final NGResource resource = new NGResource ();
25- resource ._inputStreamSupplier = inputStreamSupplier ;
26- return resource ;
27- }
20+ public InputStream inputStream ();
2821
2922 /**
3023 * @return The resource's data as a byte array
3124 */
32- public byte [] bytes () {
25+ public default byte [] bytes () {
3326
3427 try ( final InputStream is = inputStream ()) {
3528 return is .readAllBytes ();
@@ -40,19 +33,71 @@ public byte[] bytes() {
4033 }
4134
4235 /**
43- * @return The resource's data by opening a new inputStream provided by the InputStream supplier
36+ * @return A resource that obtains it's data from the given supplier
37+ */
38+ public static NGResource of ( final Callable <InputStream > inputStreamSupplier ) {
39+ return new NGInputStreamSupplierResource ( inputStreamSupplier );
40+ }
41+
42+ /**
43+ * @return A resource that obtains it's data from the given supplier
4444 */
45- public InputStream inputStream () {
46- final Callable <InputStream > provider = _inputStreamSupplier ;
45+ public static NGResource of ( final URL url ) {
46+ return new NGClasspathResource ( url );
47+ }
4748
48- try {
49- return provider .call ();
49+ public static class NGClasspathResource implements NGResource {
50+
51+ private URL _url ;
52+
53+ /**
54+ * Constructs a new classpath resource from the URL provided
55+ */
56+ public NGClasspathResource ( URL url ) {
57+ _url = url ;
5058 }
51- catch ( IOException e ) {
52- throw new UncheckedIOException ( e );
59+
60+ @ Override
61+ public InputStream inputStream () {
62+ try {
63+ return _url .openStream ();
64+ }
65+ catch ( IOException e ) {
66+ throw new UncheckedIOException ( e );
67+ }
68+ }
69+ }
70+
71+ /**
72+ * A resource obtained from the java class path
73+ */
74+ public static class NGInputStreamSupplierResource implements NGResource {
75+
76+ /**
77+ * A method that will provide us with the actual inputStream
78+ */
79+ private Callable <InputStream > _inputStreamSupplier ;
80+
81+ public NGInputStreamSupplierResource ( Callable <InputStream > inputStreamSupplier ) {
82+ _inputStreamSupplier = inputStreamSupplier ;
5383 }
54- catch ( Exception e ) {
55- throw new RuntimeException ( e );
84+
85+ /**
86+ * @return The resource's data by opening a new inputStream provided by the InputStream supplier
87+ */
88+ @ Override
89+ public InputStream inputStream () {
90+ final Callable <InputStream > provider = _inputStreamSupplier ;
91+
92+ try {
93+ return provider .call ();
94+ }
95+ catch ( IOException e ) {
96+ throw new UncheckedIOException ( e );
97+ }
98+ catch ( Exception e ) {
99+ throw new RuntimeException ( e );
100+ }
56101 }
57102 }
58103}
0 commit comments