2020import static java .util .Optional .empty ;
2121import static java .util .Optional .of ;
2222
23+ import java .io .File ;
2324import java .io .InputStream ;
25+ import java .net .URI ;
26+ import java .net .URISyntaxException ;
27+ import java .net .URL ;
28+ import java .net .URLDecoder ;
2429import java .nio .charset .Charset ;
30+ import java .util .Enumeration ;
31+ import java .util .LinkedList ;
32+ import java .util .List ;
33+ import java .util .Objects ;
2534import 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
2740import lombok .NonNull ;
41+ import lombok .SneakyThrows ;
2842import lombok .val ;
2943
3044/**
3448 */
3549public 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}
0 commit comments