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