| NetUtil.java |
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 public final class NetUtil {
59 /**
60 * Basic constructor - ensure that we can't be instantiated
61 */
62 private NetUtil() {
63 }
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 return new URI(uri.toString());
114 } catch (URISyntaxException e) {
115 assert false : e;
116 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 checkFileURI(orig);
131
132 File file = new File(orig.getPath());
133
134 // If it is a file, except
135 if (file.isFile()) {
136 // TRANSLATOR: Error condition: A directory was expected, but a file was found. {0} is a placeholder for the file.
137 throw new MalformedURLException(JSMsg.gettext("The URL {0} is a file.", orig));
138 }
139
140 // Is it already a directory ?
141 if (!file.isDirectory()) {
142 // Create the directory and make sure it worked.
143 if (!file.mkdirs()) {
144 // TRANSLATOR: Error condition: A directory could not be created. {0} is a placeholder for the directory
145 throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a directory.", orig));
146 }
147 }
148 }
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 checkFileURI(orig);
162
163 File file = new File(orig.getPath());
164
165 // If it is a file, except
166 if (file.isDirectory()) {
167 // TRANSLATOR: Error condition: A file was expected, but a directory was found. {0} is a placeholder for the directory.
168 throw new MalformedURLException(JSMsg.gettext("The URL {0} is a directory.", orig));
169 }
170
171 // Is it already a directory ?
172 if (!file.isFile()) {
173 FileOutputStream fout = new FileOutputStream(file);
174 fout.close();
175
176 // Did that work?
177 if (!file.isFile()) {
178 // TRANSLATOR: Error condition: A file could not be created. {0} is a placeholder for the file
179 throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a file.", orig));
180 }
181 }
182 }
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 if (uri.getScheme().equals(PROTOCOL_FILE)) {
193 return new File(uri.getPath()).isFile();
194 }
195
196 try {
197 // This will throw if the resource does not exist
198 uri.toURL().openStream().close();
199 return true;
200 } catch (IOException ex) {
201 // the resource does not exist!
202 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 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
216 return false;
217 }
218
219 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 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
232 return false;
233 }
234
235 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 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
248 return false;
249 }
250
251 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 checkFileURI(oldUri);
268 checkFileURI(newUri);
269
270 File oldFile = new File(oldUri.getPath());
271 File newFile = new File(newUri.getPath());
272 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 checkFileURI(orig);
286
287 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 if (uri.getScheme().equals(PROTOCOL_FILE)) {
302 return new File(uri.getPath());
303 }
304 String hashString = (uri.toString().hashCode() + "").replace('-', 'm');
305
306 // get the location of the tempWorkingDir
307 File workingDir = getURICacheDir();
308 File workingFile = null;
309
310 if (workingDir != null && workingDir.isDirectory()) {
311 workingFile = new File(workingDir, hashString);
312 } else {
313 // If there's no working dir, we just use temp...
314 workingFile = File.createTempFile(hashString, TEMP_SUFFIX);
315 }
316 workingFile.deleteOnExit();
317
318 // copy the contents of the URI to the file
319 OutputStream output = null;
320 InputStream input = null;
321 try {
322 output = new FileOutputStream(workingFile);
323 input = uri.toURL().openStream();
324 byte[] data = new byte[512];
325 for (int read = 0; read != -1; read = input.read(data)) {
326 output.write(data, 0, read);
327 }
328 } finally {
329 try {
330 if (input != null) {
331 input.close();
332 }
333 } finally {
334 if (output != null) {
335 output.close();
336 }
337 }
338 }
339
340 // return the new file in URI form
341 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 String file = orig.getPath();
357 char lastChar = file.charAt(file.length() - 1);
358 if (isSeparator(lastChar)) {
359 file = file.substring(0, file.length() - 1);
360 }
361
362 String test = file.substring(file.length() - strip.length());
363 if (!test.equals(strip)) {
364 throw new MalformedURLException(JSOtherMsg.lookupText("The URL {0} does not end in {1}.", orig, strip));
365 }
366
367 String newFile = file.substring(0, file.length() - strip.length());
368
369 try {
370 return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), newFile, "",
371 "");
372 } catch (URISyntaxException e) {
373 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 String extra = anExtra;
388 try {
389 StringBuilder path = new StringBuilder(orig.getPath());
390 char lastChar = path.charAt(path.length() - 1);
391 char firstChar = extra.charAt(0);
392 if (isSeparator(firstChar)) {
393 if (isSeparator(lastChar)) {
394 path.append(extra.substring(1));
395 } else {
396 path.append(extra);
397 }
398 } else {
399 if (!isSeparator(lastChar)) {
400 path.append(SEPARATOR);
401 }
402 path.append(extra);
403 }
404
405 return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), path.toString(), orig.getQuery(), orig.getFragment());
406 } catch (URISyntaxException ex) {
407 return null;
408 }
409 }
410
411 private static boolean isSeparator(char c) {
412 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 if (uri.getScheme().equals(PROTOCOL_FILE)) {
427 return new FileInputStream(uri.getPath());
428 }
429 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 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 if (uri.getScheme().equals(PROTOCOL_FILE)) {
464 return new FileOutputStream(uri.getPath(), append);
465 }
466 URLConnection cnx = uri.toURL().openConnection();
467 cnx.setDoOutput(true);
468 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 String[] reply = {};
493 try {
494 URI search = NetUtil.lengthenURI(uri, INDEX_FILE);
495 reply = listByIndexFile(search, filter);
496 } 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 LOGGER.warn("index file for " + uri.toString() + " was not found.");
502 if (uri.getScheme().equals(PROTOCOL_FILE)) {
503 return listByFile(uri, filter);
504 }
505 }
506
507 // if we can - check that the index file is up to date.
508 if (uri.getScheme().equals(PROTOCOL_FILE)) {
509 String[] files = listByFile(uri, filter);
510
511 // Check that the answers are the same
512 if (files.length != reply.length) {
513 LOGGER.warn("index file for {} has incorrect number of entries.", uri.toString());
514 } else {
515 List<String> list = Arrays.asList(files);
516 for (int i = 0; i < files.length; i++) {
517 if (!list.contains(files[i])) {
518 LOGGER.warn("file: based index found {} but this was not found using index file.", files[i]);
519 }
520 }
521 }
522 }
523
524 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 File fdir = new File(uri.getPath());
538 if (!fdir.isDirectory()) {
539 // TRANSLATOR: Error condition: A directory was expected, but a file was found. {0} is a placeholder for the file.
540 throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a directory", uri.toString()));
541 }
542
543 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 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 InputStream in = null;
581 BufferedReader din = null;
582 try {
583 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 din = new BufferedReader(new InputStreamReader(in), 8192);
587
588 // We still need to do the filtering
589 List<String> list = new ArrayList<String>();
590
591 while (true) {
592 String line = din.readLine();
593
594 if (line == null) {
595 break;
596 }
597
598 String name = line;
599
600 // Strip comments from the line
601 int len = name.length();
602 int commentPos;
603 for (commentPos = 0; commentPos < len && name.charAt(commentPos) != '#'; ++commentPos) {
604 continue; // test does the work
605 }
606
607 if (commentPos < len) {
608 name = name.substring(0, commentPos);
609 }
610
611 // we need to trim extraneous whitespace on the line
612 name = name.trim();
613
614 // Is it acceptable?
615 if (name.length() > 0 && !name.equals(INDEX_FILE) && filter.accept(name)) {
616 list.add(name);
617 }
618 }
619
620 return list.toArray(new String[list.size()]);
621 } finally {
622 IOUtil.close(din);
623 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 InputStream is = null;
637 try {
638 is = NetUtil.getInputStream(uri);
639 PropertyMap prop = new PropertyMap();
640 prop.load(is);
641 is.close();
642 return prop;
643 } finally {
644 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 OutputStream out = null;
662
663 try {
664 out = NetUtil.getOutputStream(uri);
665 PropertyMap temp = new PropertyMap();
666 temp.putAll(properties);
667 temp.store(out, title);
668 } finally {
669 IOUtil.close(out);
670 }
671 }
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 return getSize(uri, null, null);
680 }
681
682 public static int getSize(URI uri, String proxyHost) {
683 return getSize(uri, proxyHost, null);
684 }
685
686 public static int getSize(URI uri, String proxyHost, Integer proxyPort) {
687 try {
688 if (uri.getScheme().equals(PROTOCOL_HTTP)) {
689 WebResource resource = new WebResource(uri, proxyHost, proxyPort);
690 int size = resource.getSize();
691 resource.shutdown();
692 return size;
693 }
694
695 return uri.toURL().openConnection().getContentLength();
696 } catch (IOException e) {
697 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 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 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 if (uri.getScheme().equals(PROTOCOL_HTTP)) {
736 WebResource resource = new WebResource(uri, proxyHost, proxyPort);
737 long time = resource.getLastModified();
738 resource.shutdown();
739 return time;
740 }
741
742 URLConnection urlConnection = uri.toURL().openConnection();
743 long time = urlConnection.getLastModified();
744
745 // If it were a jar then time contains the last modified date of the jar.
746 if (urlConnection instanceof JarURLConnection) {
747 // form is jar:file:.../xxx.jar!.../filename.ext
748 JarURLConnection jarConnection = (JarURLConnection) urlConnection;
749 JarEntry jarEntry = jarConnection.getJarEntry();
750 time = jarEntry.getTime();
751 }
752
753 return time;
754 } catch (IOException ex) {
755 LOGGER.warn("Failed to get modified time", ex);
756 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 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 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 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 public URIFilterFilenameFilter(URIFilter filter) {
809 this.filter = filter;
810 }
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 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 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 throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a file.", uri));
834 }
835 }
836
837 /**
838 * Returns the cache directory.
839 *
840 * @return File
841 */
842 public static File getURICacheDir() {
843 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 NetUtil.cachedir = cachedir;
854 }
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 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 File tempFile = File.createTempFile(prefix, suffix);
878 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 return new URI(url.toExternalForm());
891 } catch (URISyntaxException e) {
892 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 return uri.toURL();
906 } catch (MalformedURLException e) {
907 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 public IsDirectoryURIFilter(URI parent) {
922 this.parent = parent;
923 }
924
925 /* (non-Javadoc)
926 * @see org.crosswire.common.util.URIFilter#accept(java.lang.String)
927 */
928 public boolean accept(String name) {
929 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 private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class);
944 }
945