Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
NetUtil |
|
| 2.8372093023255816;2.837 | ||||
NetUtil$IsDirectoryURIFilter |
|
| 2.8372093023255816;2.837 | ||||
NetUtil$URIFilterFilenameFilter |
|
| 2.8372093023255816;2.837 |
1 | /** | |
2 | * Distribution License: | |
3 | * JSword is free software; you can redistribute it and/or modify it under | |
4 | * the terms of the GNU Lesser General Public License, version 2.1 or later | |
5 | * as published by the Free Software Foundation. This program is distributed | |
6 | * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even | |
7 | * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
8 | * See the GNU Lesser General Public License for more details. | |
9 | * | |
10 | * The License is available on the internet at: | |
11 | * http://www.gnu.org/copyleft/lgpl.html | |
12 | * or by writing to: | |
13 | * Free Software Foundation, Inc. | |
14 | * 59 Temple Place - Suite 330 | |
15 | * Boston, MA 02111-1307, USA | |
16 | * | |
17 | * © CrossWire Bible Society, 2005 - 2016 | |
18 | * | |
19 | */ | |
20 | package org.crosswire.common.util; | |
21 | ||
22 | import java.io.BufferedReader; | |
23 | import java.io.File; | |
24 | import java.io.FileInputStream; | |
25 | import java.io.FileNotFoundException; | |
26 | import java.io.FileOutputStream; | |
27 | import java.io.FilenameFilter; | |
28 | import java.io.IOException; | |
29 | import java.io.InputStream; | |
30 | import java.io.InputStreamReader; | |
31 | import java.io.OutputStream; | |
32 | import java.net.JarURLConnection; | |
33 | import java.net.MalformedURLException; | |
34 | import java.net.URI; | |
35 | import java.net.URISyntaxException; | |
36 | import java.net.URL; | |
37 | import java.net.URLConnection; | |
38 | import java.util.ArrayList; | |
39 | import java.util.Arrays; | |
40 | import java.util.Date; | |
41 | import java.util.List; | |
42 | import java.util.jar.JarEntry; | |
43 | ||
44 | import org.crosswire.jsword.JSMsg; | |
45 | import org.crosswire.jsword.JSOtherMsg; | |
46 | import org.slf4j.Logger; | |
47 | import org.slf4j.LoggerFactory; | |
48 | ||
49 | /** | |
50 | * The NetUtil class looks after general utility stuff around the java.net | |
51 | * package. | |
52 | * | |
53 | * @see gnu.lgpl.License The GNU Lesser General Public License for details. | |
54 | * @author Joe Walker | |
55 | * @author Mark Goodwin | |
56 | * @author DM Smith | |
57 | */ | |
58 | 0 | public final class NetUtil { |
59 | /** | |
60 | * Basic constructor - ensure that we can't be instantiated | |
61 | */ | |
62 | 0 | private NetUtil() { |
63 | 0 | } |
64 | ||
65 | /** | |
66 | * Constant for the file: protocol or scheme | |
67 | */ | |
68 | public static final String PROTOCOL_FILE = "file"; | |
69 | ||
70 | /** | |
71 | * Constant for the http: protocol or scheme | |
72 | */ | |
73 | public static final String PROTOCOL_HTTP = "http"; | |
74 | ||
75 | /** | |
76 | * Constant for the ftp: protocol or scheme | |
77 | */ | |
78 | public static final String PROTOCOL_FTP = "ftp"; | |
79 | ||
80 | /** | |
81 | * Constant for the jar: protocol or scheme | |
82 | */ | |
83 | public static final String PROTOCOL_JAR = "jar"; | |
84 | ||
85 | /** | |
86 | * For directory listings | |
87 | */ | |
88 | public static final String INDEX_FILE = "index.txt"; | |
89 | ||
90 | /** | |
91 | * URL/URI separator | |
92 | */ | |
93 | public static final String SEPARATOR = "/"; | |
94 | ||
95 | /** | |
96 | * Separating the username from the rest of the URL/URI | |
97 | */ | |
98 | public static final String AUTH_SEPERATOR_USERNAME = "@"; | |
99 | ||
100 | /** | |
101 | * Separating the password from the username | |
102 | */ | |
103 | public static final String AUTH_SEPERATOR_PASSWORD = ":"; | |
104 | ||
105 | /** | |
106 | * The temporary suffix, used when a temporary file is needed in the | |
107 | * system's temporary directory. | |
108 | */ | |
109 | private static final String TEMP_SUFFIX = "tmp"; | |
110 | ||
111 | public static URI copy(URI uri) { | |
112 | try { | |
113 | 0 | return new URI(uri.toString()); |
114 | 0 | } catch (URISyntaxException e) { |
115 | 0 | assert false : e; |
116 | 0 | return null; |
117 | } | |
118 | } | |
119 | ||
120 | /** | |
121 | * If the directory does not exist, create it. Note this currently only | |
122 | * works with file: type URIs | |
123 | * | |
124 | * @param orig | |
125 | * The directory URI to create | |
126 | * @throws MalformedURLException | |
127 | * If the URI is not valid | |
128 | */ | |
129 | public static void makeDirectory(URI orig) throws MalformedURLException { | |
130 | 0 | checkFileURI(orig); |
131 | ||
132 | 0 | File file = new File(orig.getPath()); |
133 | ||
134 | // If it is a file, except | |
135 | 0 | if (file.isFile()) { |
136 | // TRANSLATOR: Error condition: A directory was expected, but a file was found. {0} is a placeholder for the file. | |
137 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} is a file.", orig)); |
138 | } | |
139 | ||
140 | // Is it already a directory ? | |
141 | 0 | if (!file.isDirectory()) { |
142 | // Create the directory and make sure it worked. | |
143 | 0 | if (!file.mkdirs()) { |
144 | // TRANSLATOR: Error condition: A directory could not be created. {0} is a placeholder for the directory | |
145 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a directory.", orig)); |
146 | } | |
147 | } | |
148 | 0 | } |
149 | ||
150 | /** | |
151 | * If the file does not exist, create it. Note this currently only works | |
152 | * with file: type URIs | |
153 | * | |
154 | * @param orig | |
155 | * The file URI to create | |
156 | * @throws MalformedURLException | |
157 | * If the URI is not valid | |
158 | * @throws IOException a problem with I/O happened | |
159 | */ | |
160 | public static void makeFile(URI orig) throws MalformedURLException, IOException { | |
161 | 0 | checkFileURI(orig); |
162 | ||
163 | 0 | File file = new File(orig.getPath()); |
164 | ||
165 | // If it is a file, except | |
166 | 0 | if (file.isDirectory()) { |
167 | // TRANSLATOR: Error condition: A file was expected, but a directory was found. {0} is a placeholder for the directory. | |
168 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} is a directory.", orig)); |
169 | } | |
170 | ||
171 | // Is it already a directory ? | |
172 | 0 | if (!file.isFile()) { |
173 | 0 | FileOutputStream fout = new FileOutputStream(file); |
174 | 0 | fout.close(); |
175 | ||
176 | // Did that work? | |
177 | 0 | if (!file.isFile()) { |
178 | // TRANSLATOR: Error condition: A file could not be created. {0} is a placeholder for the file | |
179 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a file.", orig)); |
180 | } | |
181 | } | |
182 | 0 | } |
183 | ||
184 | /** | |
185 | * If there is a file at the other end of this URI return true. | |
186 | * | |
187 | * @param uri | |
188 | * The URI to check | |
189 | * @return true if the URI points at a file | |
190 | */ | |
191 | public static boolean isFile(URI uri) { | |
192 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
193 | 0 | return new File(uri.getPath()).isFile(); |
194 | } | |
195 | ||
196 | try { | |
197 | // This will throw if the resource does not exist | |
198 | 0 | uri.toURL().openStream().close(); |
199 | 0 | return true; |
200 | 0 | } catch (IOException ex) { |
201 | // the resource does not exist! | |
202 | 0 | return false; |
203 | } | |
204 | } | |
205 | ||
206 | /** | |
207 | * If there is a directory at the other end of this URI return true. Note | |
208 | * non file: type URI will always return false | |
209 | * | |
210 | * @param orig | |
211 | * The URI to check | |
212 | * @return true if the URI points at a file: directory | |
213 | */ | |
214 | public static boolean isDirectory(URI orig) { | |
215 | 0 | if (!orig.getScheme().equals(PROTOCOL_FILE)) { |
216 | 0 | return false; |
217 | } | |
218 | ||
219 | 0 | return new File(orig.getPath()).isDirectory(); |
220 | } | |
221 | ||
222 | /** | |
223 | * If there is a writable directory or file at the other end of this URI | |
224 | * return true. Note non file: type URIs will always return false | |
225 | * | |
226 | * @param orig | |
227 | * The URI to check | |
228 | * @return true if the URI points at a writable file or directory | |
229 | */ | |
230 | public static boolean canWrite(URI orig) { | |
231 | 0 | if (!orig.getScheme().equals(PROTOCOL_FILE)) { |
232 | 0 | return false; |
233 | } | |
234 | ||
235 | 0 | return new File(orig.getPath()).canWrite(); |
236 | } | |
237 | ||
238 | /** | |
239 | * If there is a readable directory or file at the other end of this URI | |
240 | * return true. Note non file: type URIs will always return false | |
241 | * | |
242 | * @param orig | |
243 | * The URI to check | |
244 | * @return true if the URI points at a readable file or directory | |
245 | */ | |
246 | public static boolean canRead(URI orig) { | |
247 | 0 | if (!orig.getScheme().equals(PROTOCOL_FILE)) { |
248 | 0 | return false; |
249 | } | |
250 | ||
251 | 0 | return new File(orig.getPath()).canRead(); |
252 | } | |
253 | ||
254 | /** | |
255 | * Move a URI from one place to another. Currently this only works for file: | |
256 | * URIs, however the interface should not need to change to handle more | |
257 | * complex URIs | |
258 | * | |
259 | * @param oldUri | |
260 | * The URI to move | |
261 | * @param newUri | |
262 | * The destination URI | |
263 | * @return whether the move happened | |
264 | * @throws IOException a problem with I/O happened | |
265 | */ | |
266 | public static boolean move(URI oldUri, URI newUri) throws IOException { | |
267 | 0 | checkFileURI(oldUri); |
268 | 0 | checkFileURI(newUri); |
269 | ||
270 | 0 | File oldFile = new File(oldUri.getPath()); |
271 | 0 | File newFile = new File(newUri.getPath()); |
272 | 0 | return oldFile.renameTo(newFile); |
273 | } | |
274 | ||
275 | /** | |
276 | * Delete a URI. Currently this only works for file: URIs, however the | |
277 | * interface should not need to change to handle more complex URIs | |
278 | * | |
279 | * @param orig | |
280 | * The URI to delete | |
281 | * @return whether the deleted happened | |
282 | * @throws IOException a problem with I/O happened | |
283 | */ | |
284 | public static boolean delete(URI orig) throws IOException { | |
285 | 0 | checkFileURI(orig); |
286 | ||
287 | 0 | return new File(orig.getPath()).delete(); |
288 | } | |
289 | ||
290 | /** | |
291 | * Return a File from the URI either by extracting from a file: URI or by | |
292 | * downloading to a temp dir first | |
293 | * | |
294 | * @param uri | |
295 | * The original URI to the file. | |
296 | * @return The URI as a file | |
297 | * @throws IOException a problem with I/O happened | |
298 | */ | |
299 | public static File getAsFile(URI uri) throws IOException { | |
300 | // if the URI is already a file URI, return it | |
301 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
302 | 0 | return new File(uri.getPath()); |
303 | } | |
304 | 0 | String hashString = (uri.toString().hashCode() + "").replace('-', 'm'); |
305 | ||
306 | // get the location of the tempWorkingDir | |
307 | 0 | File workingDir = getURICacheDir(); |
308 | 0 | File workingFile = null; |
309 | ||
310 | 0 | if (workingDir != null && workingDir.isDirectory()) { |
311 | 0 | workingFile = new File(workingDir, hashString); |
312 | } else { | |
313 | // If there's no working dir, we just use temp... | |
314 | 0 | workingFile = File.createTempFile(hashString, TEMP_SUFFIX); |
315 | } | |
316 | 0 | workingFile.deleteOnExit(); |
317 | ||
318 | // copy the contents of the URI to the file | |
319 | 0 | OutputStream output = null; |
320 | 0 | InputStream input = null; |
321 | try { | |
322 | 0 | output = new FileOutputStream(workingFile); |
323 | 0 | input = uri.toURL().openStream(); |
324 | 0 | byte[] data = new byte[512]; |
325 | 0 | for (int read = 0; read != -1; read = input.read(data)) { |
326 | 0 | output.write(data, 0, read); |
327 | } | |
328 | } finally { | |
329 | 0 | try { |
330 | 0 | if (input != null) { |
331 | 0 | input.close(); |
332 | } | |
333 | } finally { | |
334 | 0 | if (output != null) { |
335 | 0 | output.close(); |
336 | } | |
337 | } | |
338 | 0 | } |
339 | ||
340 | // return the new file in URI form | |
341 | 0 | return workingFile; |
342 | } | |
343 | ||
344 | /** | |
345 | * Utility to strip a string from the end of a URI. | |
346 | * | |
347 | * @param orig | |
348 | * The URI to strip | |
349 | * @param strip | |
350 | * The text to strip from the end of the URI | |
351 | * @return The stripped URI | |
352 | * @exception MalformedURLException | |
353 | * If the URI does not end in the given text | |
354 | */ | |
355 | public static URI shortenURI(URI orig, String strip) throws MalformedURLException { | |
356 | 0 | String file = orig.getPath(); |
357 | 0 | char lastChar = file.charAt(file.length() - 1); |
358 | 0 | if (isSeparator(lastChar)) { |
359 | 0 | file = file.substring(0, file.length() - 1); |
360 | } | |
361 | ||
362 | 0 | String test = file.substring(file.length() - strip.length()); |
363 | 0 | if (!test.equals(strip)) { |
364 | 0 | throw new MalformedURLException(JSOtherMsg.lookupText("The URL {0} does not end in {1}.", orig, strip)); |
365 | } | |
366 | ||
367 | 0 | String newFile = file.substring(0, file.length() - strip.length()); |
368 | ||
369 | try { | |
370 | 0 | return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), newFile, "", |
371 | ""); | |
372 | 0 | } catch (URISyntaxException e) { |
373 | 0 | throw new MalformedURLException(JSOtherMsg.lookupText("The URL {0} does not end in {1}.", orig, strip)); |
374 | } | |
375 | } | |
376 | ||
377 | /** | |
378 | * Utility to add a string to the end of a URI. | |
379 | * | |
380 | * @param orig | |
381 | * The URI to lengthen | |
382 | * @param anExtra | |
383 | * The text to add to the end of the URI | |
384 | * @return The lengthened URI | |
385 | */ | |
386 | public static URI lengthenURI(URI orig, String anExtra) { | |
387 | 0 | String extra = anExtra; |
388 | try { | |
389 | 0 | StringBuilder path = new StringBuilder(orig.getPath()); |
390 | 0 | char lastChar = path.charAt(path.length() - 1); |
391 | 0 | char firstChar = extra.charAt(0); |
392 | 0 | if (isSeparator(firstChar)) { |
393 | 0 | if (isSeparator(lastChar)) { |
394 | 0 | path.append(extra.substring(1)); |
395 | } else { | |
396 | 0 | path.append(extra); |
397 | } | |
398 | } else { | |
399 | 0 | if (!isSeparator(lastChar)) { |
400 | 0 | path.append(SEPARATOR); |
401 | } | |
402 | 0 | path.append(extra); |
403 | } | |
404 | ||
405 | 0 | return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), path.toString(), orig.getQuery(), orig.getFragment()); |
406 | 0 | } catch (URISyntaxException ex) { |
407 | 0 | return null; |
408 | } | |
409 | } | |
410 | ||
411 | private static boolean isSeparator(char c) { | |
412 | 0 | return c == '/' || c == '\\'; |
413 | } | |
414 | ||
415 | /** | |
416 | * Attempt to obtain an InputStream from a URI. If the URI is a file scheme | |
417 | * then just open it directly. Otherwise, call uri.toURL().openStream(). | |
418 | * | |
419 | * @param uri | |
420 | * The URI to attempt to read from | |
421 | * @return An InputStream connection | |
422 | * @throws IOException a problem with I/O happened | |
423 | */ | |
424 | public static InputStream getInputStream(URI uri) throws IOException { | |
425 | // We favor the FileOutputStream | |
426 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
427 | 0 | return new FileInputStream(uri.getPath()); |
428 | } | |
429 | 0 | return uri.toURL().openStream(); |
430 | } | |
431 | ||
432 | /** | |
433 | * Attempt to obtain an OutputStream from a URI. The simple case will open | |
434 | * it if it is local. Otherwise, it will call | |
435 | * uri.toURL().openConnection().getOutputStream(), however in some JVMs (MS | |
436 | * at least this fails where new FileOutputStream(url) works. | |
437 | * | |
438 | * @param uri | |
439 | * The URI to attempt to write to | |
440 | * @return An OutputStream connection | |
441 | * @throws IOException a problem with I/O happened | |
442 | */ | |
443 | public static OutputStream getOutputStream(URI uri) throws IOException { | |
444 | 0 | return getOutputStream(uri, false); |
445 | } | |
446 | ||
447 | /** | |
448 | * Attempt to obtain an OutputStream from a URI. The simple case will open | |
449 | * it if it is local. Otherwise, it will call | |
450 | * uri.toURL().openConnection().getOutputStream(), however in some JVMs (MS | |
451 | * at least this fails where new FileOutputStream(url) works. | |
452 | * | |
453 | * @param uri | |
454 | * The URI to attempt to write to | |
455 | * @param append | |
456 | * Do we write to the end of the file instead of the beginning | |
457 | * @return An OutputStream connection | |
458 | * @throws IOException a problem with I/O happened | |
459 | */ | |
460 | public static OutputStream getOutputStream(URI uri, boolean append) throws IOException { | |
461 | // We favor the FileOutputStream method here because append | |
462 | // is not well defined for the openConnection method | |
463 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
464 | 0 | return new FileOutputStream(uri.getPath(), append); |
465 | } | |
466 | 0 | URLConnection cnx = uri.toURL().openConnection(); |
467 | 0 | cnx.setDoOutput(true); |
468 | 0 | return cnx.getOutputStream(); |
469 | } | |
470 | ||
471 | /** | |
472 | * List the items available assuming that this URI points to a directory. | |
473 | * <p> | |
474 | * There are 2 methods of calculating the answer - if the URI is a file: URI | |
475 | * then we can just use File.list(), otherwise we ask for a file inside the | |
476 | * directory called index.txt and assume the directories contents to be | |
477 | * listed one per line. | |
478 | * <p> | |
479 | * If the URI is a file: URI then we execute both methods and warn if there | |
480 | * is a difference, but returning the values from the index.txt method. | |
481 | * | |
482 | * @param uri the URI to list | |
483 | * @param filter the filter for the listing | |
484 | * @return the filtered list | |
485 | * @throws MalformedURLException | |
486 | * If the URI is not valid | |
487 | * @throws IOException a problem with I/O happened | |
488 | */ | |
489 | public static String[] list(URI uri, URIFilter filter) throws MalformedURLException, IOException { | |
490 | // We should probably cache this in some way? This is going | |
491 | // to get very slow calling this often across a network | |
492 | 0 | String[] reply = {}; |
493 | try { | |
494 | 0 | URI search = NetUtil.lengthenURI(uri, INDEX_FILE); |
495 | 0 | reply = listByIndexFile(search, filter); |
496 | 0 | } catch (FileNotFoundException ex) { |
497 | // So the index file was not found - this isn't going to work over | |
498 | // JNLP or other systems that can't use file: URIs. But it is silly | |
499 | // to get to picky so if there is a solution using file: then just | |
500 | // print a warning and carry on. | |
501 | 0 | LOGGER.warn("index file for " + uri.toString() + " was not found."); |
502 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
503 | 0 | return listByFile(uri, filter); |
504 | } | |
505 | 0 | } |
506 | ||
507 | // if we can - check that the index file is up to date. | |
508 | 0 | if (uri.getScheme().equals(PROTOCOL_FILE)) { |
509 | 0 | String[] files = listByFile(uri, filter); |
510 | ||
511 | // Check that the answers are the same | |
512 | 0 | if (files.length != reply.length) { |
513 | 0 | LOGGER.warn("index file for {} has incorrect number of entries.", uri.toString()); |
514 | } else { | |
515 | 0 | List<String> list = Arrays.asList(files); |
516 | 0 | for (int i = 0; i < files.length; i++) { |
517 | 0 | if (!list.contains(files[i])) { |
518 | 0 | LOGGER.warn("file: based index found {} but this was not found using index file.", files[i]); |
519 | } | |
520 | } | |
521 | } | |
522 | } | |
523 | ||
524 | 0 | return reply; |
525 | } | |
526 | ||
527 | /** | |
528 | * List all the files specified by the index file passed in. | |
529 | * | |
530 | * @param uri the URI to list | |
531 | * @param filter the filter for the listing | |
532 | * @return the filtered list | |
533 | * @throws MalformedURLException | |
534 | * If the URI is not valid | |
535 | */ | |
536 | public static String[] listByFile(URI uri, URIFilter filter) throws MalformedURLException { | |
537 | 0 | File fdir = new File(uri.getPath()); |
538 | 0 | if (!fdir.isDirectory()) { |
539 | // TRANSLATOR: Error condition: A directory was expected, but a file was found. {0} is a placeholder for the file. | |
540 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a directory", uri.toString())); |
541 | } | |
542 | ||
543 | 0 | return fdir.list(new URIFilterFilenameFilter(filter)); |
544 | } | |
545 | ||
546 | /** | |
547 | * List all the strings specified by the index file passed in. To be | |
548 | * acceptable it must be a non-0 length string, not commented with #, and | |
549 | * not the index file itself. | |
550 | * | |
551 | * @param index the URI to list | |
552 | * @return the list. | |
553 | * @throws IOException a problem with I/O happened | |
554 | */ | |
555 | public static String[] listByIndexFile(URI index) throws IOException { | |
556 | 0 | return listByIndexFile(index, new DefaultURIFilter()); |
557 | } | |
558 | ||
559 | /** | |
560 | * List all the files specified by the index file passed in. | |
561 | * <p>Each line is pre-processed:</p> | |
562 | * <ul> | |
563 | * <li>Ignore comments (# to end of line)</li> | |
564 | * <li>Trim spaces from line.</li> | |
565 | * <li>Ignore blank lines.</li> | |
566 | * </ul> | |
567 | * | |
568 | * To be acceptable it: | |
569 | * <ul> | |
570 | * <li> cannot be the index file itself</li> | |
571 | * <li> and must acceptable by the filter.</li> | |
572 | * </ul> | |
573 | * | |
574 | * @param index the URI to list | |
575 | * @param filter the filter for the listing | |
576 | * @return the list. | |
577 | * @throws IOException a problem with I/O happened | |
578 | */ | |
579 | public static String[] listByIndexFile(URI index, URIFilter filter) throws IOException { | |
580 | 0 | InputStream in = null; |
581 | 0 | BufferedReader din = null; |
582 | try { | |
583 | 0 | in = NetUtil.getInputStream(index); |
584 | // Quiet Android from complaining about using the default BufferReader buffer size. | |
585 | // The actual buffer size is undocumented. So this is a good idea any way. | |
586 | 0 | din = new BufferedReader(new InputStreamReader(in), 8192); |
587 | ||
588 | // We still need to do the filtering | |
589 | 0 | List<String> list = new ArrayList<String>(); |
590 | ||
591 | while (true) { | |
592 | 0 | String line = din.readLine(); |
593 | ||
594 | 0 | if (line == null) { |
595 | 0 | break; |
596 | } | |
597 | ||
598 | 0 | String name = line; |
599 | ||
600 | // Strip comments from the line | |
601 | 0 | int len = name.length(); |
602 | int commentPos; | |
603 | 0 | for (commentPos = 0; commentPos < len && name.charAt(commentPos) != '#'; ++commentPos) { |
604 | continue; // test does the work | |
605 | } | |
606 | ||
607 | 0 | if (commentPos < len) { |
608 | 0 | name = name.substring(0, commentPos); |
609 | } | |
610 | ||
611 | // we need to trim extraneous whitespace on the line | |
612 | 0 | name = name.trim(); |
613 | ||
614 | // Is it acceptable? | |
615 | 0 | if (name.length() > 0 && !name.equals(INDEX_FILE) && filter.accept(name)) { |
616 | 0 | list.add(name); |
617 | } | |
618 | 0 | } |
619 | ||
620 | 0 | return list.toArray(new String[list.size()]); |
621 | } finally { | |
622 | 0 | IOUtil.close(din); |
623 | 0 | IOUtil.close(in); |
624 | } | |
625 | } | |
626 | ||
627 | /** | |
628 | * Load up properties given by a URI. | |
629 | * | |
630 | * @param uri | |
631 | * the location of the properties | |
632 | * @return the properties given by the URI | |
633 | * @throws IOException a problem with I/O happened | |
634 | */ | |
635 | public static PropertyMap loadProperties(URI uri) throws IOException { | |
636 | 0 | InputStream is = null; |
637 | try { | |
638 | 0 | is = NetUtil.getInputStream(uri); |
639 | 0 | PropertyMap prop = new PropertyMap(); |
640 | 0 | prop.load(is); |
641 | 0 | is.close(); |
642 | 0 | return prop; |
643 | } finally { | |
644 | 0 | IOUtil.close(is); |
645 | } | |
646 | } | |
647 | ||
648 | /** | |
649 | * Store the properties at the location given by the uri using the supplied | |
650 | * title. | |
651 | * | |
652 | * @param properties | |
653 | * the properties to store | |
654 | * @param uri | |
655 | * the location of the store | |
656 | * @param title | |
657 | * the label held in the properties file | |
658 | * @throws IOException a problem with I/O happened | |
659 | */ | |
660 | public static void storeProperties(PropertyMap properties, URI uri, String title) throws IOException { | |
661 | 0 | OutputStream out = null; |
662 | ||
663 | try { | |
664 | 0 | out = NetUtil.getOutputStream(uri); |
665 | 0 | PropertyMap temp = new PropertyMap(); |
666 | 0 | temp.putAll(properties); |
667 | 0 | temp.store(out, title); |
668 | } finally { | |
669 | 0 | IOUtil.close(out); |
670 | 0 | } |
671 | 0 | } |
672 | ||
673 | /** | |
674 | * @param uri | |
675 | * the resource whose size is wanted | |
676 | * @return the size of that resource | |
677 | */ | |
678 | public static int getSize(URI uri) { | |
679 | 0 | return getSize(uri, null, null); |
680 | } | |
681 | ||
682 | public static int getSize(URI uri, String proxyHost) { | |
683 | 0 | return getSize(uri, proxyHost, null); |
684 | } | |
685 | ||
686 | public static int getSize(URI uri, String proxyHost, Integer proxyPort) { | |
687 | try { | |
688 | 0 | if (uri.getScheme().equals(PROTOCOL_HTTP)) { |
689 | 0 | WebResource resource = new WebResource(uri, proxyHost, proxyPort); |
690 | 0 | int size = resource.getSize(); |
691 | 0 | resource.shutdown(); |
692 | 0 | return size; |
693 | } | |
694 | ||
695 | 0 | return uri.toURL().openConnection().getContentLength(); |
696 | 0 | } catch (IOException e) { |
697 | 0 | return 0; |
698 | } | |
699 | } | |
700 | ||
701 | /** | |
702 | * When was the given URI last modified. If no modification time is | |
703 | * available then this method return the current time. | |
704 | * | |
705 | * @param uri the URI to examine | |
706 | * @return the last modified date | |
707 | */ | |
708 | public static long getLastModified(URI uri) { | |
709 | 0 | return getLastModified(uri, null, null); |
710 | } | |
711 | ||
712 | /** | |
713 | * When was the given URI last modified. If no modification time is | |
714 | * available then this method return the current time. | |
715 | * | |
716 | * @param uri the URI to examine | |
717 | * @param proxyHost the proxy host | |
718 | * @return the last modified date | |
719 | */ | |
720 | public static long getLastModified(URI uri, String proxyHost) { | |
721 | 0 | return getLastModified(uri, proxyHost, null); |
722 | } | |
723 | ||
724 | /** | |
725 | * When was the given URI last modified. If no modification time is | |
726 | * available then this method return the current time. | |
727 | * | |
728 | * @param uri the URI to examine | |
729 | * @param proxyHost the proxy host | |
730 | * @param proxyPort the proxy port | |
731 | * @return the last modified date | |
732 | */ | |
733 | public static long getLastModified(URI uri, String proxyHost, Integer proxyPort) { | |
734 | try { | |
735 | 0 | if (uri.getScheme().equals(PROTOCOL_HTTP)) { |
736 | 0 | WebResource resource = new WebResource(uri, proxyHost, proxyPort); |
737 | 0 | long time = resource.getLastModified(); |
738 | 0 | resource.shutdown(); |
739 | 0 | return time; |
740 | } | |
741 | ||
742 | 0 | URLConnection urlConnection = uri.toURL().openConnection(); |
743 | 0 | long time = urlConnection.getLastModified(); |
744 | ||
745 | // If it were a jar then time contains the last modified date of the jar. | |
746 | 0 | if (urlConnection instanceof JarURLConnection) { |
747 | // form is jar:file:.../xxx.jar!.../filename.ext | |
748 | 0 | JarURLConnection jarConnection = (JarURLConnection) urlConnection; |
749 | 0 | JarEntry jarEntry = jarConnection.getJarEntry(); |
750 | 0 | time = jarEntry.getTime(); |
751 | } | |
752 | ||
753 | 0 | return time; |
754 | 0 | } catch (IOException ex) { |
755 | 0 | LOGGER.warn("Failed to get modified time", ex); |
756 | 0 | return new Date().getTime(); |
757 | } | |
758 | } | |
759 | ||
760 | /** | |
761 | * Returns whether the left is newer than the right by comparing their last | |
762 | * modified dates. | |
763 | * | |
764 | * @param left one URI to compare | |
765 | * @param right the other URI to compare | |
766 | * @return true if the left is newer | |
767 | */ | |
768 | public static boolean isNewer(URI left, URI right) { | |
769 | 0 | return isNewer(left, right, null, null); |
770 | } | |
771 | ||
772 | /** | |
773 | * Returns whether the left is newer than the right by comparing their last | |
774 | * modified dates. | |
775 | * | |
776 | * @param left one URI to compare | |
777 | * @param right the other URI to compare | |
778 | * @param proxyHost the proxy host | |
779 | * @return true if the left is newer | |
780 | */ | |
781 | public static boolean isNewer(URI left, URI right, String proxyHost) { | |
782 | 0 | return isNewer(left, right, proxyHost, null); |
783 | } | |
784 | ||
785 | /** | |
786 | * Returns whether the left is newer than the right by comparing their last | |
787 | * modified dates. | |
788 | * | |
789 | * @param left one URI to compare | |
790 | * @param right the other URI to compare | |
791 | * @param proxyHost the proxy host | |
792 | * @param proxyPort the proxy port | |
793 | * @return true if the left is newer | |
794 | */ | |
795 | public static boolean isNewer(URI left, URI right, String proxyHost, Integer proxyPort) { | |
796 | 0 | return NetUtil.getLastModified(left, proxyHost, proxyPort) > NetUtil.getLastModified(right, proxyHost, proxyPort); |
797 | } | |
798 | ||
799 | /** | |
800 | * Quick implementation of FilenameFilter that uses a URIFilter | |
801 | */ | |
802 | public static class URIFilterFilenameFilter implements FilenameFilter { | |
803 | /** | |
804 | * Simple ctor | |
805 | * | |
806 | * @param filter the filter | |
807 | */ | |
808 | 0 | public URIFilterFilenameFilter(URIFilter filter) { |
809 | 0 | this.filter = filter; |
810 | 0 | } |
811 | ||
812 | /* (non-Javadoc) | |
813 | * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String) | |
814 | */ | |
815 | public boolean accept(File arg0, String name) { | |
816 | 0 | return filter.accept(name); |
817 | } | |
818 | ||
819 | private URIFilter filter; | |
820 | } | |
821 | ||
822 | /** | |
823 | * Throw if the given URI does not use the 'file:' protocol | |
824 | * | |
825 | * @param uri | |
826 | * The URI to check | |
827 | * @throws MalformedURLException | |
828 | * If the protocol is not file: | |
829 | */ | |
830 | private static void checkFileURI(URI uri) throws MalformedURLException { | |
831 | 0 | if (!uri.getScheme().equals(PROTOCOL_FILE)) { |
832 | // TRANSLATOR: Error condition: The URL protocol "file:" was expected, but something else was found. {0} is a placeholder for the URL. | |
833 | 0 | throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a file.", uri)); |
834 | } | |
835 | 0 | } |
836 | ||
837 | /** | |
838 | * Returns the cache directory. | |
839 | * | |
840 | * @return File | |
841 | */ | |
842 | public static File getURICacheDir() { | |
843 | 0 | return cachedir; |
844 | } | |
845 | ||
846 | /** | |
847 | * Sets the cache directory. | |
848 | * | |
849 | * @param cachedir | |
850 | * The cache directory to set | |
851 | */ | |
852 | public static void setURICacheDir(File cachedir) { | |
853 | 0 | NetUtil.cachedir = cachedir; |
854 | 0 | } |
855 | ||
856 | /** | |
857 | * Get a URI version of the given file. | |
858 | * | |
859 | * @param file | |
860 | * The File to turn into a URI | |
861 | * @return a URI for the given file | |
862 | */ | |
863 | public static URI getURI(File file) { | |
864 | 0 | return file.toURI(); |
865 | } | |
866 | ||
867 | /** | |
868 | * A URI version of <code>File.createTempFile()</code> | |
869 | * | |
870 | * @param prefix the prefix of the temporary file | |
871 | * @param suffix the suffix of the temporary file | |
872 | * @return A new temporary URI | |
873 | * @throws IOException | |
874 | * If something goes wrong creating the temp URI | |
875 | */ | |
876 | public static URI getTemporaryURI(String prefix, String suffix) throws IOException { | |
877 | 0 | File tempFile = File.createTempFile(prefix, suffix); |
878 | 0 | return getURI(tempFile); |
879 | } | |
880 | ||
881 | /** | |
882 | * Convert an URL to an URI. | |
883 | * | |
884 | * @param url | |
885 | * to convert | |
886 | * @return the URI representation of the URL | |
887 | */ | |
888 | public static URI toURI(URL url) { | |
889 | try { | |
890 | 0 | return new URI(url.toExternalForm()); |
891 | 0 | } catch (URISyntaxException e) { |
892 | 0 | return null; |
893 | } | |
894 | } | |
895 | ||
896 | /** | |
897 | * Convert an URI to an URL. | |
898 | * | |
899 | * @param uri | |
900 | * to convert | |
901 | * @return the URL representation of the URI | |
902 | */ | |
903 | public static URL toURL(URI uri) { | |
904 | try { | |
905 | 0 | return uri.toURL(); |
906 | 0 | } catch (MalformedURLException e) { |
907 | 0 | return null; |
908 | } | |
909 | } | |
910 | ||
911 | /** | |
912 | * Check that the directories in the version directory really represent | |
913 | * versions. | |
914 | */ | |
915 | public static class IsDirectoryURIFilter implements URIFilter { | |
916 | /** | |
917 | * Simple ctor | |
918 | * | |
919 | * @param parent the parent directory | |
920 | */ | |
921 | 0 | public IsDirectoryURIFilter(URI parent) { |
922 | 0 | this.parent = parent; |
923 | 0 | } |
924 | ||
925 | /* (non-Javadoc) | |
926 | * @see org.crosswire.common.util.URIFilter#accept(java.lang.String) | |
927 | */ | |
928 | public boolean accept(String name) { | |
929 | 0 | return NetUtil.isDirectory(NetUtil.lengthenURI(parent, name)); |
930 | } | |
931 | ||
932 | private URI parent; | |
933 | } | |
934 | ||
935 | /** | |
936 | * Where are temporary files cached. | |
937 | */ | |
938 | private static File cachedir; | |
939 | ||
940 | /** | |
941 | * The log stream | |
942 | */ | |
943 | 0 | private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class); |
944 | } |