Skip to content

Commit bdf1663

Browse files
Artem LabazinArtem Labazin
authored andcommitted
Add new resource utils functions
1 parent 290cc8c commit bdf1663

File tree

12 files changed

+296
-5
lines changed

12 files changed

+296
-5
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
- Add more tests.
1313
- Add `JavaDoc`.
1414

15+
## [1.10.0](https://github.com/appulse-projects/utils-java/releases/tag/1.10.0) - 2018-09-25
16+
17+
### Added
18+
19+
- `FifoCache` implementation;
20+
- `ResourceUtils.getResourceUrls` function for flexible searching resource files;
21+
- `ResourceUtils.get*Content` functions for extracting resource files content;
22+
- A test for checking `ResourceUtils.getResourceUrls` and `ResourceUtils.get*Content` functions.
23+
24+
### Changed
25+
26+
- `ResourceUtils.getResource` are deprecated, use `ResourceUtils.get*Content` functions;
27+
- `LruCache` and `FifoCache` moved to `io.appulse.utils.cache` package.
28+
1529
## [1.9.0](https://github.com/appulse-projects/utils-java/releases/tag/1.9.0) - 2018-09-11
1630

1731
### Added

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ limitations under the License.
2424

2525
<groupId>io.appulse</groupId>
2626
<artifactId>utils-java</artifactId>
27-
<version>1.9.0</version>
27+
<version>1.10.0</version>
2828
<packaging>jar</packaging>
2929

3030
<properties>
@@ -62,7 +62,7 @@ limitations under the License.
6262
<url>https://github.com/appulse-projects/utils-java</url>
6363
<connection>scm:git:https://github.com/appulse-projects/utils-java.git</connection>
6464
<developerConnection>scm:git:https://github.com/appulse-projects/utils-java.git</developerConnection>
65-
<tag>1.9.0</tag>
65+
<tag>1.10.0</tag>
6666
</scm>
6767

6868
<distributionManagement>

src/main/java/io/appulse/utils/ResourceUtils.java

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,25 @@
2020
import static java.util.Optional.empty;
2121
import static java.util.Optional.of;
2222

23+
import java.io.File;
2324
import java.io.InputStream;
25+
import java.net.URI;
26+
import java.net.URISyntaxException;
27+
import java.net.URL;
28+
import java.net.URLDecoder;
2429
import java.nio.charset.Charset;
30+
import java.util.Enumeration;
31+
import java.util.LinkedList;
32+
import java.util.List;
33+
import java.util.Objects;
2534
import java.util.Optional;
35+
import java.util.jar.JarEntry;
36+
import java.util.jar.JarFile;
37+
import java.util.regex.Pattern;
38+
import java.util.stream.Stream;
2639

2740
import lombok.NonNull;
41+
import lombok.SneakyThrows;
2842
import lombok.val;
2943

3044
/**
@@ -34,11 +48,76 @@
3448
*/
3549
public final class ResourceUtils {
3650

51+
@Deprecated
3752
public static Optional<String> getResource (@NonNull String name) {
3853
return getResource(name, UTF_8);
3954
}
4055

56+
@Deprecated
4157
public static Optional<String> getResource (@NonNull String name, @NonNull Charset charset) {
58+
return getTextContent(name, charset);
59+
}
60+
61+
/**
62+
* Returns bytes content of a resource file by its URL.
63+
*
64+
* @param url adress to a resource.
65+
*
66+
* @return file's bytes representation, if exists, or empty.
67+
*
68+
* @since 1.10.0
69+
*/
70+
public static Optional<byte[]> getBytesContent (@NonNull URL url) {
71+
try {
72+
InputStream inputStream = url.openStream();
73+
byte[] bytes = BytesUtils.read(inputStream);
74+
return of(bytes);
75+
} catch (Exception ex) {
76+
return empty();
77+
}
78+
}
79+
80+
/**
81+
* Returns text content of a resource file by its URL.
82+
*
83+
* @param url adress to a resource.
84+
*
85+
* @param charset text content charset.
86+
*
87+
* @return file's text representation, if exists, or empty.
88+
*
89+
* @since 1.10.0
90+
*/
91+
public static Optional<String> getTextContent (@NonNull URL url, @NonNull Charset charset) {
92+
return getBytesContent(url)
93+
.map(it -> new String(it, charset));
94+
}
95+
96+
/**
97+
* Returns a concrete resource content by its name.
98+
*
99+
* @param name file's name
100+
*
101+
* @return file's text representation, if exists, or empty.
102+
*
103+
* @since 1.10.0
104+
*/
105+
public static Optional<String> getTextContent (@NonNull String name) {
106+
return getTextContent(name, UTF_8);
107+
}
108+
109+
/**
110+
* Returns a concrete resource content by its name.
111+
*
112+
* @param name file's name
113+
*
114+
* @param charset text content charset.
115+
*
116+
* @return file's text representation, if exists, or empty.
117+
*
118+
* @since 1.10.0
119+
*/
120+
public static Optional<String> getTextContent (@NonNull String name, @NonNull Charset charset) {
42121
val classLoader = Thread.currentThread().getContextClassLoader();
43122
InputStream inputStream = classLoader.getResourceAsStream(name);
44123
if (inputStream == null) {
@@ -53,6 +132,127 @@ public static Optional<String> getResource (@NonNull String name, @NonNull Chars
53132
return of(string);
54133
}
55134

135+
/**
136+
* Returns text content of a resource file by its URL.
137+
*
138+
* @param url adress to a resource.
139+
*
140+
* @return file's text representation, if exists, or empty.
141+
*
142+
* @since 1.10.0
143+
*/
144+
public static Optional<String> getTextContent (@NonNull URL url) {
145+
return getTextContent(url, UTF_8);
146+
}
147+
148+
/**
149+
* Searches resource files in specific folder and by glob-pattern.
150+
* <p>
151+
* It uses glob-like pattern sysytem...honestly speaking only '*' and '?' signs.
152+
*
153+
* @param folder where to search the files
154+
*
155+
* @param glob files name glob-pattern
156+
*
157+
* @return the URLs of found files
158+
*
159+
* @since 1.10.0
160+
*/
161+
public static List<URL> getResourceUrls (@NonNull String folder, @NonNull String glob) {
162+
Pattern pattern = of(glob)
163+
.map(Pattern::quote)
164+
.map(it -> it.replace("*", "\\E.*\\Q"))
165+
.map(it -> it.replace("?", "\\E.\\Q"))
166+
.map(it -> '^' + it + '$')
167+
.map(Pattern::compile)
168+
.orElseThrow(RuntimeException::new);
169+
170+
return getResourceUrls(folder, pattern);
171+
}
172+
173+
/**
174+
* Searches resource files in specific folder and by pattern.
175+
*
176+
* @param folder where to search the files
177+
*
178+
* @param pattern files name pattern
179+
*
180+
* @return the URLs of found files
181+
*
182+
* @since 1.10.0
183+
*/
184+
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
185+
@SneakyThrows
186+
public static List<URL> getResourceUrls (@NonNull String folder, @NonNull Pattern pattern) {
187+
List<URL> result = new LinkedList<>();
188+
189+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
190+
Enumeration<URL> urls = classLoader.getResources(folder);
191+
192+
while (urls.hasMoreElements()) {
193+
URL url = urls.nextElement();
194+
if (url == null) {
195+
continue;
196+
}
197+
198+
switch (url.getProtocol()) {
199+
case "file":
200+
toFile(url)
201+
.map(file -> file.isFile()
202+
? new File[]{ file }
203+
: file.listFiles()
204+
)
205+
.filter(Objects::nonNull)
206+
.ifPresent(files -> Stream.of(files)
207+
.filter(it -> pattern.matcher(it.getName()).matches())
208+
.map(it -> {
209+
try {
210+
return it.toURI().toURL();
211+
} catch (Exception ex) {
212+
return null;
213+
}
214+
})
215+
.filter(Objects::nonNull)
216+
.forEach(result::add));
217+
break;
218+
case "jar":
219+
String dirname = of(folder)
220+
.filter(it -> it.endsWith("/"))
221+
.orElseGet(() -> folder + '/');
222+
223+
String path = url.getPath();
224+
String jarPath = path.substring(5, path.indexOf('!'));
225+
226+
try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, UTF_8.name()))) {
227+
jar.stream()
228+
.map(JarEntry::getName)
229+
.filter(it -> it.startsWith(dirname))
230+
.filter(it -> pattern.matcher(it).matches())
231+
.map(it -> classLoader.getResource(it))
232+
.forEach(result::add);
233+
}
234+
break;
235+
default:
236+
throw new UnsupportedOperationException("unsupported protocol " + url.getProtocol());
237+
}
238+
}
239+
240+
return result;
241+
}
242+
243+
private static Optional<File> toFile (URL url) throws URISyntaxException {
244+
URI uri = url.toURI();
245+
try {
246+
return of(new File(uri));
247+
} catch (Exception ex1) {
248+
try {
249+
return of(new File(uri.getPath()));
250+
} catch (Exception ex2) {
251+
return empty();
252+
}
253+
}
254+
}
255+
56256
private ResourceUtils () {
57257
}
58258
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.appulse.utils.cache;
18+
19+
import static lombok.AccessLevel.PRIVATE;
20+
21+
import java.util.LinkedHashMap;
22+
import java.util.Map.Entry;
23+
24+
import lombok.experimental.FieldDefaults;
25+
26+
27+
/**
28+
* FIFO cache implementation based on {@code LinkedHashMap}.
29+
*
30+
* @author Artem Labazin
31+
* @since 1.10.0
32+
*/
33+
@FieldDefaults(level = PRIVATE, makeFinal = true)
34+
public class FifoCache<K, V> extends LinkedHashMap<K, V> {
35+
36+
private static final long serialVersionUID = 2326368924657010098L;
37+
38+
int maxSize;
39+
40+
public FifoCache (int maxSize) {
41+
super(maxSize + 1, 1.0F, false);
42+
this.maxSize = maxSize;
43+
}
44+
45+
@Override
46+
protected boolean removeEldestEntry (Entry<K, V> eldest) {
47+
return size() > maxSize;
48+
}
49+
}

src/main/java/io/appulse/utils/LruCache.java renamed to src/main/java/io/appulse/utils/cache/LruCache.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,28 @@
1414
* limitations under the License.
1515
*/
1616

17-
package io.appulse.utils;
17+
package io.appulse.utils.cache;
18+
19+
import static lombok.AccessLevel.PRIVATE;
1820

1921
import java.util.LinkedHashMap;
2022
import java.util.Map.Entry;
2123

24+
import lombok.experimental.FieldDefaults;
25+
26+
2227
/**
23-
* LRU cache implementation based on {@code LinkedHashMap}
28+
* LRU cache implementation based on {@code LinkedHashMap}.
2429
*
2530
* @author Artem Labazin
2631
* @since 1.9.0
2732
*/
33+
@FieldDefaults(level = PRIVATE, makeFinal = true)
2834
public class LruCache<K, V> extends LinkedHashMap<K, V> {
2935

3036
private static final long serialVersionUID = -1100634446524987320L;
3137

32-
private final int maxSize;
38+
int maxSize;
3339

3440
public LruCache (int maxSize) {
3541
super(maxSize + 1, 1.0F, true);

src/test/java/io/appulse/utils/ResourceUtilsTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
package io.appulse.utils;
1818

19+
import static java.util.stream.Collectors.toList;
1920
import static org.assertj.core.api.Assertions.assertThat;
2021

22+
import java.util.List;
23+
import java.util.Optional;
24+
2125
import org.junit.Test;
2226

2327
/**
@@ -39,4 +43,16 @@ public void notExist () {
3943
assertThat(ResourceUtils.getResource("/not-exist.txt"))
4044
.isNotPresent();
4145
}
46+
47+
@Test
48+
public void getResourceUrls () {
49+
List<String> list = ResourceUtils.getResourceUrls("folder", "file-?.*")
50+
.stream()
51+
.map(ResourceUtils::getTextContent)
52+
.map(Optional::get)
53+
.map(String::trim)
54+
.collect(toList());
55+
56+
assertThat(list).contains("Artem", "Liza", "Milada", "Thais");
57+
}
4258
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Artem
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
popa
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Liza
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Milada

0 commit comments

Comments
 (0)