Skip to content

Commit 7ebc4cd

Browse files
Vladimir.ShapkinVladimir.Shapkin
Vladimir.Shapkin
authored and
Vladimir.Shapkin
committed
Added support for getting a list of files from a jar directory in ResourceOf.
1 parent ea5712d commit 7ebc4cd

File tree

10 files changed

+285
-5
lines changed

10 files changed

+285
-5
lines changed

pom.xml

+7
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ SOFTWARE.
106106
</exclusion>
107107
</exclusions>
108108
</dependency>
109+
<dependency>
110+
<groupId>files</groupId>
111+
<artifactId>test-data</artifactId>
112+
<version>0.0.1</version>
113+
<scope>system</scope>
114+
<systemPath>${basedir}/test-data/test-data-0.0.1.jar</systemPath>
115+
</dependency>
109116
<dependency>
110117
<groupId>com.google.code.findbugs</groupId>
111118
<artifactId>annotations</artifactId>

src/main/java/org/cactoos/io/ResourceOf.java

+93-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525

2626
import java.io.IOException;
2727
import java.io.InputStream;
28+
import java.net.URISyntaxException;
29+
import java.net.URL;
30+
import java.nio.charset.StandardCharsets;
31+
import java.util.ArrayList;
32+
import java.util.Enumeration;
33+
import java.util.List;
34+
import java.util.jar.JarEntry;
35+
import java.util.jar.JarFile;
36+
import java.util.stream.Collectors;
2837
import org.cactoos.Func;
2938
import org.cactoos.Input;
3039
import org.cactoos.Text;
@@ -225,10 +234,18 @@ public InputStream stream() throws Exception {
225234
"The \"classloader\" is NULL, which is not allowed"
226235
);
227236
}
228-
InputStream input = this.loader.getResourceAsStream(
229-
this.path.asString()
230-
);
231-
if (input == null) {
237+
final InputStream input;
238+
final String pth = this.path.asString();
239+
final URL resource = this.loader.getResource(pth);
240+
if (pth.endsWith("/")
241+
&& resource != null
242+
&& "jar".equals(resource.getProtocol())
243+
) {
244+
input = new JarDirectoryFileNameStream(resource, pth)
245+
.files();
246+
} else if (resource != null) {
247+
input = resource.openStream();
248+
} else {
232249
if (this.fallback == null) {
233250
throw new IllegalArgumentException(
234251
"The \"fallback\" is NULL, which is not allowed"
@@ -240,4 +257,76 @@ public InputStream stream() throws Exception {
240257
}
241258
return input;
242259
}
260+
261+
/**
262+
* Class for creating a stream of file names from a directory to a jar.
263+
*
264+
* @since 0.56.2
265+
*/
266+
private static final class JarDirectoryFileNameStream {
267+
268+
/**
269+
* URL of the jar file.
270+
*/
271+
private final URL url;
272+
273+
/**
274+
* Path to the directory in the jar file.
275+
*/
276+
private final String path;
277+
278+
/**
279+
* Ctor.
280+
*
281+
* @param jarurl URL of the jar file.
282+
* @param pth The directory for which we want to get a list of files.
283+
*/
284+
JarDirectoryFileNameStream(final URL jarurl, final String pth) {
285+
this.url = jarurl;
286+
this.path = pth;
287+
}
288+
289+
/**
290+
* Create InputStream of file names from directory to jar.
291+
*
292+
* @return Stream with file names.
293+
* @throws Exception If something goes wrong
294+
*/
295+
public InputStream files() throws Exception {
296+
final List<String> names = new ArrayList<>(2);
297+
try (JarFile jar = new JarFile(this.extract())) {
298+
final Enumeration<JarEntry> entries = jar.entries();
299+
while (entries.hasMoreElements()) {
300+
final JarEntry entry = entries.nextElement();
301+
final String name = entry.getName();
302+
if (this.path.equals(name)) {
303+
continue;
304+
} else if (name.lastIndexOf(this.path) >= 0) {
305+
names.add(name.substring(this.path.length()));
306+
}
307+
}
308+
}
309+
return
310+
new InputStreamOf(
311+
names
312+
.stream()
313+
.collect(
314+
Collectors.joining("\n")
315+
)
316+
.getBytes(StandardCharsets.UTF_8)
317+
);
318+
}
319+
320+
/**
321+
* Extracts the path to a jar file from a URL.
322+
*
323+
* @return Path to jar file.
324+
* @throws URISyntaxException If this URL cannot be converted to a URI.
325+
*/
326+
private String extract() throws URISyntaxException {
327+
final String fullpath = this.url.toURI().getSchemeSpecificPart();
328+
final int idx = fullpath.indexOf("!/");
329+
return fullpath.substring("file:".length(), idx);
330+
}
331+
}
243332
}

src/test/java/org/cactoos/io/ResourceOfTest.java

+63-1
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@
2828
import org.cactoos.Text;
2929
import org.cactoos.bytes.BytesOf;
3030
import org.cactoos.text.FormattedText;
31+
import org.cactoos.text.Split;
3132
import org.cactoos.text.TextOf;
3233
import org.hamcrest.core.IsEqual;
3334
import org.junit.jupiter.api.Test;
3435
import org.llorllale.cactoos.matchers.Assertion;
3536
import org.llorllale.cactoos.matchers.EndsWith;
37+
import org.llorllale.cactoos.matchers.HasValues;
3638
import org.llorllale.cactoos.matchers.StartsWith;
3739
import org.llorllale.cactoos.matchers.Throws;
3840

@@ -42,7 +44,7 @@
4244
* @since 0.1
4345
* @checkstyle JavadocMethodCheck (500 lines)
4446
*/
45-
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
47+
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.TooManyMethods"})
4648
final class ResourceOfTest {
4749

4850
@Test
@@ -113,6 +115,66 @@ void throwsWhenResourceIsAbsent() {
113115
).affirm();
114116
}
115117

118+
@Test
119+
void readTextFromJar() {
120+
new Assertion<>(
121+
"Can't to read file from jar",
122+
new TextOf(
123+
new BytesOf(
124+
new ResourceOf(
125+
"org/cactoos/io/small-text-file.txt",
126+
"the replacement"
127+
)
128+
)
129+
),
130+
new EndsWith("parent directory.")
131+
).affirm();
132+
}
133+
134+
@Test
135+
void readDirectoryFromJar() {
136+
new Assertion<>(
137+
"Unable to read file names from jar directory",
138+
new Split(
139+
new TextOf(
140+
new BytesOf(
141+
new ResourceOf(
142+
"org/cactoos/io/dir/",
143+
"the replacement"
144+
)
145+
)
146+
),
147+
new TextOf("\\n")
148+
),
149+
new HasValues<>(
150+
new TextOf("second-text-file.txt"),
151+
new TextOf("small-file-in-dir.txt")
152+
)
153+
).affirm();
154+
}
155+
156+
@Test
157+
void readDirectory() {
158+
new Assertion<>(
159+
"Unable to read file names from directory",
160+
new Split(
161+
new TextOf(
162+
new BytesOf(
163+
new ResourceOf(
164+
"org/cactoos/",
165+
"the replacement"
166+
)
167+
)
168+
),
169+
new TextOf("\\n")
170+
),
171+
new HasValues<>(
172+
new TextOf("digest-calculation.txt"),
173+
new TextOf("small-text.txt")
174+
)
175+
).affirm();
176+
}
177+
116178
@Test
117179
void acceptsTextAsResourceName() {
118180
new Assertion<>(

test-data/LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016-2025 Objectionary.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included
13+
in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

test-data/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
This package contains test data that is builded into a jar for use in tests in the base project.
2+

test-data/pom.xml

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
The MIT License (MIT)
4+
5+
Copyright (c) 2017-2025 Yegor Bugayenko
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included
15+
in all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
-->
25+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
26+
<modelVersion>4.0.0</modelVersion>
27+
<groupId>files</groupId>
28+
<artifactId>test-data</artifactId>
29+
<version>0.0.1</version>
30+
<packaging>jar</packaging>
31+
<name>test-data</name>
32+
<description>Data for testing</description>
33+
<url>https://github.com/yegor256/cactoos</url>
34+
<inceptionYear>2017</inceptionYear>
35+
<organization>
36+
<name>Cactoos</name>
37+
<url>https://github.com/yegor256/cactoos</url>
38+
</organization>
39+
<licenses>
40+
<license>
41+
<name>MIT</name>
42+
<url>https://raw.githubusercontent.com/yegor256/cactoos/master/LICENSE.txt</url>
43+
<distribution>site</distribution>
44+
</license>
45+
</licenses>
46+
<developers>
47+
<developer>
48+
<id>1</id>
49+
<name>Yegor Bugayenko</name>
50+
<email>[email protected]</email>
51+
<organization>yegor256.com</organization>
52+
<organizationUrl>https://www.yegor256.com</organizationUrl>
53+
<roles>
54+
<role>Architect</role>
55+
<role>Developer</role>
56+
</roles>
57+
<timezone>+3</timezone>
58+
</developer>
59+
</developers>
60+
<issueManagement>
61+
<system>GitHub</system>
62+
<url>https://github.com/yegor256/cactoos/issues</url>
63+
</issueManagement>
64+
<scm>
65+
<connection>scm:git:[email protected]:yegor256/cactoos.git</connection>
66+
<developerConnection>scm:git:[email protected]:yegor256/cactoos.git</developerConnection>
67+
<url>https://github.com/yegor256/cactoos</url>
68+
</scm>
69+
<ciManagement>
70+
<system>rultor</system>
71+
<url>https://www.rultor.com/s/cactoos</url>
72+
</ciManagement>
73+
<distributionManagement>
74+
<site>
75+
<id>github-pages</id>
76+
<url>https://github.com/yegor256/cactoos</url>
77+
</site>
78+
</distributionManagement>
79+
<build>
80+
<plugins>
81+
<plugin>
82+
<artifactId>maven-jar-plugin</artifactId>
83+
<configuration>
84+
<archive>
85+
<index>true</index>
86+
<manifestEntries>
87+
<TEST-Version>0.0.1</TEST-Version>
88+
<TEST-Revision>0.0.1</TEST-Revision>
89+
<TEST-Dob>2025-01-14T19:26:39</TEST-Dob>
90+
</manifestEntries>
91+
</archive>
92+
</configuration>
93+
</plugin>
94+
</plugins>
95+
</build>
96+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Second text file.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Small file in the directory.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Small file in the parent directory.

test-data/test-data-0.0.1.jar

3.23 KB
Binary file not shown.

0 commit comments

Comments
 (0)