Source code: javax/ide/net/VirtualFileSystem.java
1 /*
2 * @(#)VirtualFileSystem.java
3 *
4 * Copyright 2003 by Oracle Corporation,
5 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
6 * All rights reserved.
7 *
8 * This software is the confidential and proprietary information
9 * of Oracle Corporation.
10 */
11
12 package javax.ide.net;
13
14 import java.io.File;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19
20 import java.net.MalformedURLException;
21 import java.net.URI;
22 import java.net.URL;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29
30 import javax.ide.spi.ProviderNotFoundException;
31 import javax.ide.Service;
32
33 /**
34 * The <code>VirtualFileSystem</code> class is responsible for encapsulating
35 * the notion of file system operations on content that is pointed to by
36 * an {@link URI}.<P>
37 *
38 * The behavior of <code>VirtualFileSystem</code> can be extended by
39 * subclasses of {@link VirtualFileSystemHelper}. An instance of
40 * <code>VirtualFileSystemHelper</code> is registered with
41 * <code>VirtualFileSystem</code> in association with a particular
42 * scheme. Scheme-specific behavior can thus be encapsulated by a
43 * specific implementation of <code>VirtualFileSystemHelper</code>.
44 *
45 * IDE implementations do not need to register an implementation of this
46 * service. The default registered implementation delegates most of its
47 * operations to registered VirtualFileSystemHelpers.
48 */
49 public class VirtualFileSystem extends Service
50 {
51
52 private static final int COPY_BUFFER_SIZE = 4096;
53
54 /**
55 * The "file" URI scheme.
56 */
57 public static final String FILE_SCHEME = "file"; // NOTRANS
58 /**
59 * The "http" URI scheme.
60 */
61 public static final String HTTP_SCHEME = "http"; // NOTRANS
62 /**
63 * The "jar" URI scheme.
64 */
65 public static final String JAR_SCHEME = "jar"; // NOTRANS
66
67 private static final boolean _isCaseSensitive;
68
69 private final HashMap _helpers = new HashMap();
70 private final VirtualFileSystemHelper _defaultHelper =
71 new VirtualFileSystemHelper();
72 private final ArrayList _existsTests = new ArrayList( 3 );
73
74
75 static
76 {
77 // Determine whether the local file system is case-sensitive or not.
78 // This will affect how URIs are sorted in the UI.
79 final File f1 = new File( "A" );
80 final File f2 = new File( "a" );
81 _isCaseSensitive = !f1.equals( f2 );
82 }
83
84 //--------------------------------------------------------------------------
85 // extension API...
86 //--------------------------------------------------------------------------
87 /**
88 * Registers the specified {@link VirtualFileSystemHelper} as the object
89 * that can handle {@link VirtualFileSystem} operations for {@link URI}s
90 * of the specified <CODE>scheme</CODE>.<P>
91 *
92 * If the <CODE>scheme</CODE> is <CODE>null</CODE> or the empty
93 * string or if <CODE>helper</CODE> is <CODE>null</CODE>, this method
94 * does nothing.
95 *
96 * @param scheme the URI scheme to register a helper for. Must not be null
97 * or an empty String.
98 * @param helper the helper to register for the specified scheme. Must not
99 * be null.
100 */
101 public void registerHelper( String scheme, VirtualFileSystemHelper helper )
102 {
103 if ( scheme == null )
104 {
105 throw new NullPointerException( "scheme must not be null" );
106 }
107 // Trim whitespace from the scheme name because it is not significant.
108 scheme = scheme.trim();
109 if ( scheme.length() == 0 )
110 {
111 throw new IllegalArgumentException( "cannot use empty string for scheme" );
112 }
113 if ( helper == null )
114 {
115 throw new NullPointerException( "helper must not be null" );
116 }
117
118 _helpers.put( scheme, helper );
119 }
120
121 /**
122 * Returns the {@link VirtualFileSystemHelper} class that is currently
123 * registered to handle operations related to the specified
124 * <CODE>scheme</CODE>. If there is no registered helper, then a
125 * default helper is returned that can produce a default result.
126 *
127 * @param scheme the scheme to look up the helper for. Must not be null.
128 * @return the registered file system helper for the specified scheme, or
129 * a default helper if no helper is registered.
130 */
131 public VirtualFileSystemHelper findHelper( String scheme )
132 {
133 if ( scheme == null )
134 {
135 throw new NullPointerException( "scheme must not be null" );
136 }
137 Object helper = _helpers.get( scheme );
138 return helper != null ? (VirtualFileSystemHelper) helper : _defaultHelper;
139 }
140
141 /**
142 * Returns the {@link VirtualFileSystemHelper} class that is currently
143 * registered to handle operations related to the specified
144 * {@link URI}. If there is no registered helper, then a default
145 * helper is returned that can produce a default result.
146 *
147 * @param uri a uri to find the helper for. May be null, in which case
148 * the default helper is returned.
149 * @return the helper for the scheme of the specified uri, or the
150 * default helper if the uri is null or no helper is registered for the
151 * scheme of the uri.
152 */
153 public VirtualFileSystemHelper findHelper( URI uri )
154 {
155 if ( uri == null )
156 {
157 return _defaultHelper;
158 }
159
160 final String scheme = uri.getScheme();
161 return findHelper( scheme );
162 }
163
164 /**
165 * Add an implementation of the {@link URIExistsTest} interface.
166 * The <code>existsTest</code> object will be called by the
167 * <code>isBound()</code> method to determine if an {@link URI} is unique.
168 *
169 * @param existsTest the implementation of an existence test for uris. Must
170 * not be null.
171 */
172 public void addExistsTest( URIExistsTest existsTest )
173 {
174 if ( existsTest == null )
175 {
176 throw new NullPointerException( "existsTest must not be null" );
177 }
178 _existsTests.add( existsTest );
179 }
180
181 //--------------------------------------------------------------------------
182 // VirtualFileSystem operations...
183 //--------------------------------------------------------------------------
184 /**
185 * Returns a canonical form of the {@link URI}, if one is available.
186 *
187 * @param uri the uri to canonicalize.
188 * @return a canonicalized form of the specified uri.
189 * @throws java.io.IOException if the uri could not be canonicalized.
190 * @see VirtualFileSystemHelper#canonicalize( URI )
191 */
192 public URI canonicalize( URI uri ) throws IOException
193 {
194 return findHelper( uri ).canonicalize( uri );
195 }
196
197 /**
198 * Tests whether the application can read the resource at the
199 * specified {@link URI}.
200 *
201 * @param uri the uri to check.
202 * @return <CODE>true</CODE> if and only if the specified
203 * {@link URI} points to a resource that exists <EM>and</EM> can be
204 * read by the application; <CODE>false</CODE> otherwise.
205 * @see VirtualFileSystemHelper#canRead( URI )
206 */
207 public boolean canRead( URI uri )
208 {
209 return findHelper( uri ).canRead( uri );
210 }
211
212 /**
213 * Tests whether the application can modify the resource at the
214 * specified {@link URI}.
215 *
216 * @param uri the uri to check.
217 * @return <CODE>true</CODE> if and only if the specified
218 * {@link URI} points to a file that exists <EM>and</EM> the
219 * application is allowed to write to the file; <CODE>false</CODE>
220 * otherwise.
221 * @see VirtualFileSystemHelper#canWrite( URI )
222 */
223 public boolean canWrite( URI uri )
224 {
225 return findHelper( uri ).canWrite( uri );
226 }
227
228 /**
229 * Tests whether the application can create the resource at the specified
230 * {@link URI}. This method tests that all components of the path can
231 * be created. If the resource pointed by the {@link URI} is read-only,
232 * this method returns <CODE>false</CODE>.
233 *
234 * @param uri the uri to check.
235 * @return <CODE>true</CODE> if the resource at the specified {@link URI}
236 * exists or can be created; <CODE>false</CODE> otherwise.
237 * @see VirtualFileSystemHelper#canCreate( java.net.URI )
238 */
239 public boolean canCreate( URI uri )
240 {
241 return findHelper( uri ).canCreate( uri );
242 }
243
244 /**
245 * Tests whether the specified {@link URI} is valid. If the resource
246 * pointed by the {@link URI} exists the method returns <CODE>true</CODE>.
247 * If the resource does not exist, the method tests that all components
248 * of the path can be created.
249 *
250 * @param uri the uri to check.
251 * @return <CODE>true</CODE> if the {@link URI} is valid.
252 * @see VirtualFileSystemHelper#isValid( java.net.URI )
253 */
254 public boolean isValid( URI uri )
255 {
256 return findHelper( uri ).isValid( uri );
257 }
258
259 /**
260 * Takes the given {@link URI} and checks if its {@link #toString()}
261 * representation ends with the specified <CODE>oldSuffix</CODE>. If
262 * it does, the suffix is replaced with <CODE>newSuffix</CODE>. Both
263 * suffix parameters must include the leading dot ('.') if the dot is
264 * part of the suffix. If the specified {@link URI} does not end
265 * with the <CODE>oldSuffix</CODE>, then the <CODE>newSuffix</CODE>
266 * is simply appended to the end of the original {@link URI}.
267 *
268 * @param uri the uri to check.
269 * @param oldSuffix the old suffix to check for.
270 * @param newSuffix the new suffix to use.
271 *
272 * @return a new uri with the old suffix replaced by the new suffix, or the
273 * new suffix appended if the original uri did not end with
274 * <tt>oldSuffix</tt>.
275 *
276 * @see VirtualFileSystemHelper#convertSuffix( URI, String, String )
277 */
278 public URI convertSuffix( URI uri, String oldSuffix, String newSuffix )
279 {
280 return findHelper( uri ).convertSuffix( uri, oldSuffix, newSuffix );
281 }
282
283 /**
284 * Copies the contents at <CODE>src</CODE> to <CODE>dst</CODE>. If the
285 * destination directory does not exist, it will be created.
286 * IOException is thrown if:
287 * <ul>
288 * <li>failed to read the source contents</li>
289 * <li>failed to create destination directory</li>
290 * <li>failed to write source contents to the destination file</li>
291 * </ul>
292 *
293 * @param src the uri of the resource to copy from. Must not be null.
294 * @param dst the uri of the resource to copy to. Must not be null.
295 * @throws java.io.IOException if an error occurs copying the resource from
296 * the old uri to the new uri.
297 */
298 public void copy( URI src, URI dst ) throws IOException
299 {
300 if ( src == null )
301 {
302 throw new NullPointerException( "src uri must not be null" );
303 }
304 if ( dst == null )
305 {
306 throw new NullPointerException( "dst uri must not be null" );
307 }
308
309 InputStream in = null;
310 OutputStream out = null;
311 try
312 {
313 in = openInputStream( src );
314 out = openOutputStream( dst );
315 if (!mkdirs( dst ))
316 {
317 throw new IOException( "Failed to create parent directories for "+dst);
318 }
319 copy( in, out );
320 }
321 finally
322 {
323 try { if ( in != null ) in.close(); } catch ( IOException e ) { e.printStackTrace(); }
324 try { if ( out != null ) out.close(); } catch ( IOException e ) { e.printStackTrace(); }
325 }
326 }
327
328
329 /**
330 * Copies the contents of <CODE>in</CODE> to <CODE>dst</CODE>.
331 *
332 * @param in an input stream to read data from. Must not be null.
333 * @param dst a uri to copy data to. Must not be null.
334 * @throws java.io.IOException if an error occurs reading or writing.
335 */
336 public void copy( InputStream in, URI dst ) throws IOException
337 {
338 if ( in == null )
339 {
340 throw new NullPointerException( "in must not be null" );
341 }
342 if ( dst == null )
343 {
344 throw new NullPointerException( "dst must not be null" );
345 }
346
347 OutputStream out = null;
348 try
349 {
350 out = openOutputStream( dst );
351 mkdirs( dst );
352 copy( in, out );
353 }
354 finally
355 {
356 try { if ( in != null ) in.close(); } catch ( IOException e ) { e.printStackTrace(); }
357 try { if ( out != null ) out.close(); } catch ( IOException e ) { e.printStackTrace(); }
358 }
359 }
360
361
362 /**
363 * Copies the contents of <CODE>src</CODE> to <CODE>dst</CODE>.
364 *
365 * @param src the uri of a resource to read data from. Must not be null.
366 * @param dst a file object to write data to. Must not be null.
367 * @throws java.io.IOException if an error occurs copying data.
368 */
369 public void copy( URI src, File dst ) throws IOException
370 {
371 if ( src == null )
372 {
373 throw new NullPointerException( "src must not be null" );
374 }
375 if ( dst == null )
376 {
377 throw new NullPointerException( "dst must not be null" );
378 }
379
380 InputStream in = null;
381 OutputStream out = null;
382 try
383 {
384 in = openInputStream( src );
385 out = new FileOutputStream( dst );
386 dst.mkdirs();
387 copy( in, out );
388 }
389 finally
390 {
391 try { if ( in != null ) in.close(); } catch ( IOException e ) { e.printStackTrace(); }
392 try { if ( out != null ) out.close(); } catch ( IOException e ) { e.printStackTrace(); }
393 }
394 }
395
396
397 /**
398 * Common code for copy routines. By convention, the streams are
399 * closed in the same method in which they were opened. Thus,
400 * this method does not close the streams when the copying is done.
401 */
402 private static void copy( InputStream in, OutputStream out )
403 throws IOException
404 {
405 final byte[] buffer = new byte[ COPY_BUFFER_SIZE ];
406 while ( true )
407 {
408 final int bytesRead = in.read( buffer );
409 if ( bytesRead < 0 )
410 {
411 break;
412 }
413 out.write( buffer, 0, bytesRead );
414 }
415 }
416
417 /**
418 * Deletes the resource pointed to by the specified {@link URI}. If
419 * the resource is a file (or analogous to a file), then the file is
420 * removed from its directory (or container). If the content is a
421 * directory (or analogous to a directory), then the directory is
422 * removed only if it is empty (i.e. contains no other files or
423 * directories).
424 *
425 * @param uri the uri of the resource to delete.
426 * @return <CODE>true</CODE> if and only if the file or directory
427 * is successfully deleted; <CODE>false</CODE> otherwise.
428 * @see VirtualFileSystemHelper#delete( URI )
429 */
430 public boolean delete( URI uri )
431 {
432 return findHelper( uri ).delete( uri );
433 }
434
435
436 /**
437 * This method ensures that the specified {@link URI} ends with the
438 * specified <CODE>suffix</CODE>. The suffix does not necessarily
439 * have to start with a ".", so if a leading "." is required, the
440 * specified suffix must contain it -- e.g. ".java", ".class".<P>
441 *
442 * If the {@link URI} already ends in the specified suffix, then
443 * the {@link URI} itself is returned. Otherwise, a new
444 * {@link URI} is created with the the specified suffix appended
445 * to the original {@link URI}'s path part, and the new {@link URI}
446 * is returned.
447 *
448 * @param uri the uri to check. Must not be null.
449 * @param suffix the suffix to ensure the specified uri has. Must not be
450 * null.
451 *
452 * @return An {@link URI}, based on the specified {@link URI}, whose
453 * path part ends with the specified suffix.
454 */
455 public URI ensureSuffix( URI uri, String suffix )
456 {
457 if ( uri == null )
458 {
459 throw new NullPointerException( "uri must not be null" );
460 }
461 if ( suffix == null )
462 {
463 throw new NullPointerException( "suffix must not be null" );
464 }
465 return findHelper( uri ).ensureSuffix( uri, suffix );
466 }
467
468
469 /**
470 * Returns <CODE>true</CODE> if both of the specified {@link URI}
471 * parameters point to the same {@link URI} instance or if
472 * both {@link URI} parameters have the same scheme and the
473 * scheme helper determines that the {@link URI} objects are
474 * equal.
475 *
476 * @param uri1 the first uri to compare.
477 * @param uri2 the second uri to compare.
478 *
479 * @return true if the two uris are both null or the same instance, or if
480 * the schemes are equal and the registered VirtualFileSystemHelper
481 * for the scheme returns true from
482 * {@link VirtualFileSystemHelper#equals( URI, URI ) equals(uri1,uri2)}.
483 * @see VirtualFileSystemHelper#equals( URI, URI )
484 */
485 public boolean equals( URI uri1, URI uri2 )
486 {
487 if ( uri1 == uri2 )
488 {
489 return true;
490 }
491 else if ( !schemesAreEqual( uri1, uri2 ) )
492 {
493 return false;
494 }
495 return findHelper( uri1 ).equals( uri1, uri2 );
496 }
497
498
499 /**
500 * Tests whether a resource at the specified {@link URI} location
501 * currently exists. The test for existence only checks the actual
502 * location and does not check any in-memory caches. To employ
503 * additional existential tests that do check in-memory caches, see
504 * {@link #isBound(URI)}.
505 *
506 * @param uri the uri of a resource to check for existence. Must not be
507 * null.
508 * @return <CODE>true</CODE> if and only if a resource already exists
509 * at the specified {@link URI} location; <CODE>false</CODE>
510 * otherwise.
511 * @see VirtualFileSystemHelper#exists( URI )
512 */
513 public boolean exists( URI uri )
514 {
515 if ( uri == null )
516 {
517 throw new NullPointerException( "uri must not be null" );
518 }
519 return findHelper( uri ).exists( uri );
520 }
521
522
523 /**
524 * Returns the name of the file contained by the {@link URI}, not
525 * including any scheme, authority, directory path, query, or
526 * fragment. This simply returns the simple filename. For example,
527 * if you pass in an {@link URI} whose string representation is:
528 *
529 * <BLOCKQUOTE><CODE>
530 * scheme://userinfo@host:1010/dir1/dir2/file.ext?query#fragment
531 * </CODE></BLOCKQUOTE>
532 *
533 * the returned value is "<CODE>file.ext</CODE>" (without the
534 * quotes).
535 *
536 * @param uri the uri to get the file name of. Must not be null.
537 * @return The simple filename of the specified {@link URI}. This
538 * value should only be used for display purposes and not for opening
539 * streams or otherwise trying to locate the document.
540 * @see VirtualFileSystemHelper#getFileName( URI )
541 */
542 public String getFileName( URI uri )
543 {
544 if ( uri == null )
545 {
546 throw new NullPointerException( "uri must not be null" );
547 }
548 return findHelper( uri ).getFileName( uri );
549 }
550
551
552 /**
553 * Returns the number of bytes contained in the resource that the
554 * specified {@link URI} points to. If the length cannot be
555 * determined, <CODE>-1</CODE> is returned.<P>
556 *
557 * @param uri the uri of the resource to get the size of. Must not be null.
558 * @return the size in bytes of the document at the specified
559 * {@link URI}.
560 * @see VirtualFileSystemHelper#getLength( URI )
561 */
562 public long getLength( URI uri )
563 {
564 if ( uri == null )
565 {
566 throw new NullPointerException( "uri must not be null" );
567 }
568 return findHelper( uri ).getLength( uri );
569 }
570
571
572 /**
573 * Returns the name of the file contained by the {@link URI}, not
574 * including any scheme, authority, directory path, file extension,
575 * query, or fragment. This simply returns the simple filename. For
576 * example, if you pass in an {@link URI} whose string representation
577 * is:
578 *
579 * <BLOCKQUOTE><CODE>
580 * scheme://userinfo@host:1010/dir1/dir2/file.ext1.ext2?query#fragment
581 * </CODE></BLOCKQUOTE>
582 *
583 * the returned value is "<CODE>file</CODE>" (without the quotes).<P>
584 *
585 * The returned file name should only be used for display purposes
586 * and not for opening streams or otherwise trying to locate the
587 * resource indicated by the {@link URI}.<P>
588 *
589 * @param uri the uri to get the name of. Must not be null.
590 * @return the simple name of the uri without an extension.
591 * @see VirtualFileSystemHelper#getName( URI )
592 */
593 public String getName( URI uri )
594 {
595 if ( uri == null )
596 {
597 throw new NullPointerException( "uri must not be null" );
598 }
599 return findHelper( uri ).getName( uri );
600 }
601
602
603 /**
604 * Returns the {@link URI} representing the parent of the specified
605 * {@link URI}. If there is no parent then <CODE>null</CODE> is returned.
606 *
607 * @param uri the uri of the resource to get the parent of. Must not be
608 * null.
609 * @return the uri of the parent resource, or null if the specified resource
610 * has no parent.
611 *
612 * @see VirtualFileSystemHelper#getParent( URI )
613 */
614 public URI getParent( URI uri )
615 {
616 if ( uri == null )
617 {
618 throw new NullPointerException( "uri must not be null" );
619 }
620 return findHelper( uri ).getParent( uri );
621 }
622
623 /**
624 * Returns the path part of the {@link URI}. The returned string
625 * is acceptable to use in one of the {@link URIFactory} methods
626 * that takes a path.<P>
627 *
628 * This implementation delegates to {@link URI#getPath()}.
629 *
630 * @param uri the uri to get the path of. Must not be null.
631 * @return the path of the specified uri.
632 *
633 * @see VirtualFileSystemHelper#getPath( URI )
634 */
635 public String getPath( URI uri )
636 {
637 if ( uri == null )
638 {
639 throw new NullPointerException( "uri must not be null" );
640 }
641 return findHelper( uri ).getPath( uri );
642 }
643
644
645 /**
646 * Returns the path part of the {@link URI} without the last file
647 * extension. To clarify, the following examples demonstrate the
648 * different cases:
649 *
650 * <TABLE BORDER COLS=2 WIDTH="100%">
651 * <TR>
652 * <TD><CENTER>Path part of input {@link URI}</CENTER></TD>
653 * <TD><CENTER>Output {@link String}</CENTER</TD>
654 * </TR>
655 * <TR>
656 * <TD><CODE>/dir/file.ext</CODE></TD>
657 * <TD><CODE>/dir/file</CODE></TD>
658 * </TR>
659 * <TR>
660 * <TD><CODE>/dir/file.ext1.ext2</CODE></TD>
661 * <TD><CODE>/dir/file.ext1</CODE></TD>
662 * </TR>
663 * <TR>
664 * <TD><CODE>/dir1.ext1/dir2.ext2/file.ext1.ext2</CODE></TD>
665 * <TD><CODE>/dir1.ext1/dir2.ext2/file.ext1</CODE></TD>
666 * </TR>
667 * <TR>
668 * <TD><CODE>/file.ext</CODE></TD>
669 * <TD><CODE>/file</CODE></TD>
670 * </TR>
671 * <TR>
672 * <TD><CODE>/dir.ext/file</CODE></TD>
673 * <TD><CODE>/dir.ext/file</CODE></TD>
674 * </TR>
675 * <TR>
676 * <TD><CODE>/dir/file</CODE></TD>
677 * <TD><CODE>/dir/file</CODE></TD>
678 * </TR>
679 * <TR>
680 * <TD><CODE>/file</CODE></TD>
681 * <TD><CODE>/file</CODE></TD>
682 * </TR>
683 * <TR>
684 * <TD><CODE>/.ext</CODE></TD>
685 * <TD><CODE>/</CODE></TD>
686 * </TR>
687 * </TABLE>
688 *
689 * @param uri the uri to get the path of without extension. Must not be null.
690 * @return the path of the specified uri without its extension.
691 *
692 * @see VirtualFileSystemHelper#getPathNoExt( URI )
693 */
694 public String getPathNoExt( URI uri )
695 {
696 if ( uri == null )
697 {
698 throw new NullPointerException( "uri must not be null" );
699 }
700 return findHelper( uri ).getPathNoExt( uri );
701 }
702
703
704 /**
705 * Returns the platform-dependent String representation of the
706 * {@link URI}; the returned string should be considered acceptable
707 * for users to read. In general, the returned string should omit
708 * as many parts of the {@link URI} as possible. For the "file"
709 * scheme, therefore, the platform pathname should just be the
710 * pathname alone (no scheme) using the appropriate file separator
711 * character for the current platform. For other schemes, it may
712 * be necessary to reformat the {@link URI} string into a more
713 * human-readable form. That decision is left to each
714 * {@link VirtualFileSystemHelper} implementation.
715 *
716 * @param uri the uri of the resource to get the platform path name of.
717 * Must not be null.
718 * @return The path portion of the specified {@link URI} in
719 * platform-dependent notation. This value should only be used for
720 * display purposes and not for opening streams or otherwise trying
721 * to locate the document.
722 *
723 * @see VirtualFileSystemHelper#getPlatformPathName( URI )
724 */
725 public String getPlatformPathName( URI uri )
726 {
727 if ( uri == null )
728 {
729 throw new NullPointerException( "uri must not be null" );
730 }
731 return findHelper( uri ).getPlatformPathName( uri );
732 }
733
734
735 /**
736 * If a dot ('.') occurs in the filename part of the path part of
737 * the {@link URI}, then all of the text starting at the last dot is
738 * returned, including the dot. If the last dot is also the last
739 * character in the path, then the dot by itself is returned. If
740 * there is no dot in the file name (even if a dot occurs elsewhere
741 * in the path), then the empty string is returned.<P>
742 *
743 * Examples:
744 * <UL>
745 * <LI>file:/home/jsr198/foo.txt returns ".txt"
746 * <LI>file:/home/jsr198/foo.txt.bak returns ".bak"
747 * <LI>file:/home/jsr198/foo returns ""
748 * <LI>file:/home/jsr198/foo. returns "."
749 * <LI>file:/home/jsr198/foo/ returns ""
750 * <LI>file:/home/jsr198.1/foo returns ""
751 * <LI>file:/home/jsr198.1/foo.txt returns ".txt"
752 * </UL>
753 *
754 * @param uri the uri to get the suffix of. Must not be null.
755 * @return the suffix of the specified uri.
756 * @see VirtualFileSystemHelper#getSuffix( URI )
757 */
758 public String getSuffix( URI uri )
759 {
760 if ( uri == null )
761 {
762 throw new NullPointerException( "uri must not be null" );
763 }
764 return findHelper( uri ).getSuffix( uri );
765 }
766
767
768 /**
769 * Returns <CODE>true</CODE> if the path part of the {@link URI}
770 * ends with the given <CODE>suffix</CODE> String. The suffix can
771 * be any String and doesn't necessarily have to be one that begins
772 * with a dot ('.'). If you are trying to test whether the path
773 * part of the {@link URI} ends with a particular file extension,
774 * then the <CODE>suffix</CODE> parameter must begin with a '.'
775 * character to ensure that you get the right return value.
776 *
777 * @param uri the uri to check. Must not be null.
778 * @param suffix the suffix to check for. Must not be null.
779 * @return true if the specified suffix is present on the specified uri.
780 * @see VirtualFileSystemHelper#hasSuffix( URI, String )
781 */
782 public boolean hasSuffix( URI uri, String suffix )
783 {
784 if ( uri == null )
785 {
786 throw new NullPointerException( "uri must not be null" );
787 }
788 if ( suffix == null )
789 {
790 throw new NullPointerException( "suffix must not be null" );
791 }
792 return findHelper( uri ).hasSuffix( uri, suffix );
793 }
794
795
796 /**
797 * Returns <CODE>true</CODE> if <CODE>uri1</CODE> represents a
798 * a directory and <CODE>uri2</CODE> points to a location within
799 * <CODE>uri1</CODE>'s directory tree.
800 *
801 * @param uri1 the uri of a directory resource.
802 * @param uri2 the uri of a resource.
803 *
804 * @return true if uri2 is uri1 or points to a descendent resource of
805 * uri1. If either uri is null, returns false.
806 * @see VirtualFileSystemHelper#isBaseURIFor( URI, URI )
807 */
808 public boolean isBaseURIFor( URI uri1, URI uri2 )
809 {
810 if ( uri1 == null || uri2 == null )
811 {
812 return false;
813 }
814 else if ( uri1 == uri2 )
815 {
816 return true;
817 }
818 else if ( !schemesAreEqual( uri1, uri2 ) )
819 {
820 return false;
821 }
822 return findHelper( uri1 ).isBaseURIFor( uri1, uri2 );
823 }
824
825
826 /**
827 * This method tests whether the specified {@link URI} is bound to
828 * an existing resource, which may reside in memory (not yet
829 * saved) or already exist at the {@link URI} location. This is
830 * similar to {@link #exists(URI)} but differs in that additional
831 * tests are applied that check whether the {@link URI} is bound
832 * to an in-memory object before the actual {@link URI} location is
833 * checked.<P>
834 *
835 * More precisely, "bound" means either that an {@link URI} is being
836 * used in such a way that a registered {@link URIExistsTest} is able
837 * to detect the {@link URI}'s usage or that a document already
838 * exists at the {@link URI} location as determined by {@link
839 * #exists(URI)}).<P>
840 *
841 * Checking for uniqueness of a newly generated {@link URI} is the
842 * primary use for this method.
843 *
844 * @param uri the uri to check, must not be null.
845 * @return true if the specified uri is bound.
846 */
847 public boolean isBound( URI uri )
848 {
849 if ( uri == null )
850 {
851 throw new NullPointerException( "uri must not be null" );
852 }
853 for ( Iterator iter = _existsTests.iterator(); iter.hasNext(); )
854 {
855 final URIExistsTest test = (URIExistsTest) iter.next();
856 if ( test.uriExists( uri ) )
857 {
858 return true;
859 }
860 }
861 return exists( uri );
862 }
863
864
865 /**
866 * Returns <CODE>true</CODE> if the local file system is case
867 * sensitive. Returns <CODE>false</CODE> if the local file system is
868 * not case sensitive.
869 *
870 * @return true if the local file system is case sensitive.
871 */
872 public static boolean isLocalFileSystemCaseSensitive()
873 {
874 return _isCaseSensitive;
875 }
876
877
878 /**
879 * Tests whether the location indicated by the {@link URI} is
880 * a directory resource.<P>
881 *
882 * @param uri the uri to check. Must not be null.
883 * @return <CODE>true</CODE> if and only if the location indicated
884 * by the {@link URI} exists <EM>and</EM> is a directory;
885 * <CODE>false</CODE> otherwise.
886 * @see VirtualFileSystemHelper#isDirectory( URI )
887 */
888 public boolean isDirectory( URI uri )
889 {
890 if ( uri == null )
891 {
892 throw new NullPointerException( "uri must not be null" );
893 }
894 return findHelper( uri ).isDirectory( uri );
895 }
896
897
898 /**
899 * Tests whether the location indicated by the {@link URI}
900 * represents a directory path. The directory path specified by
901 * the {@link URI} need not exist.<P>
902 *
903 * This method is intended to be a higher performance version of
904 * the {@link #isDirectory(URI)} method. Implementations of this
905 * method should attempt to ascertain whether the specified {@link
906 * URI} represents a directory path by simply examining the {@link
907 * URI} itself. Time consuming i/o operations should be
908 * avoided.<P>
909 *
910 * @param uri the uri to check. Must not be null.
911 * @return <CODE>true</CODE> if the location indicated by the
912 * {@link URI} represents a directory path; the directory path need
913 * not exist.
914 *
915 * @see VirtualFileSystemHelper#isDirectoryPath( URI )
916 */
917 public boolean isDirectoryPath( URI uri )
918 {
919 if ( uri == null )
920 {
921 throw new NullPointerException( "uri must not be null" );
922 }
923 return findHelper( uri ).isDirectoryPath( uri );
924 }
925
926
927 /**
928 * Tests whether the resource indiciated by the {@link URI} is a
929 * hidden file. The exact definition of <EM>hidden</EM> is
930 * scheme-dependent and possibly system-dependent. On UNIX
931 * systems, a file is considered to be hidden if its name begins
932 * with a period character ('.'). On Win32 systems, a file is
933 * considered to be hidden if it has been marked as such in the
934 * file system.<P>
935 *
936 * @param uri the uri to check. Must not be null.
937 * @return true if the uri represents a hidden resource.
938 *
939 * @see VirtualFileSystemHelper#isHidden( URI )
940 */
941 public boolean isHidden( URI uri )
942 {
943 if ( uri == null )
944 {
945 throw new NullPointerException( "uri must not be null" );
946 }
947 return findHelper( uri ).isHidden( uri );
948 }
949
950
951 /**
952 * Returns <CODE>true</CODE> if the resource is read-only. A return
953 * value of <CODE>false</CODE> means that trying to get an
954 * {@link OutputStream} or trying to write to an {@link OutputStream}
955 * based on the {@link URI} will cause an IOException to be thrown.
956 * If the read-only status cannot be determined for some reason,
957 * this method returns <CODE>true</CODE>.
958 *
959 * @param uri the uri of the resource to check. Must not be null.
960 * @return <CODE>true</CODE> if the document pointed to by the
961 * specified {@link URI} is read-only or if the read-only status
962 * cannot be determined; <CODE>false</CODE> otherwise.
963 *
964 * @see VirtualFileSystemHelper#isReadOnly( URI )
965 */
966 public boolean isReadOnly( URI uri )
967 {
968 if ( uri == null )
969 {
970 throw new NullPointerException( "uri must not be null" );
971 }
972 return findHelper( uri ).isReadOnly( uri );
973 }
974
975
976 /**
977 * Tests whether the resource indiciated by the {@link URI} is
978 * a regular file. A <EM>regular</EM> is a file that is not a
979 * directory and, in addition, satisfies other system-dependent
980 * criteria.<P>
981 *
982 * @param uri the uri of the resource to check. Must not be null.
983 * @return <CODE>true</CODE> if and only if the resource
984 * indicated by the {@link URI} exists <EM>and</EM> is a normal
985 * file.
986 * @see VirtualFileSystemHelper#isRegularFile( URI )
987 */
988 public boolean isRegularFile( URI uri )
989 {
990 if ( uri == null )
991 {
992 throw new NullPointerException( "uri must not be null" );
993 }
994 return findHelper( uri ).isRegularFile( uri );
995 }
996
997
998 /**
999 * Returns <CODE>true</CODE> if the specified {@link URI}
1000 * corresponds to the root of a file system; <CODE>false</CODE>
1001 * otherwise.
1002 *
1003 * @param uri the uri of a resource to check. Must not be null.
1004 * @return <tt>true</tt> if the specified uri is a resource that is the
1005 * root of a file system.
1006 */
1007 public boolean isRoot( URI uri )
1008 {
1009 if ( uri == null )
1010 {
1011 throw new NullPointerException( "uri must not be null" );
1012 }
1013 final URI[] roots = listRoots();
1014 final int n = roots.length;
1015 for ( int i = 0; i < n; i++ )
1016 {
1017 if ( equals( uri, roots[i] ) )
1018 {
1019 return true;
1020 }
1021 }
1022 return false;
1023 }
1024
1025
1026 /**
1027 * Returns the last modified time of the resource pointed to by the
1028 * {@link URI}. The returned <CODE>long</CODE> is the number of
1029 * milliseconds since the epoch (00:00:00 GMT Jan 1, 1970). If no
1030 * timestamp is available, <CODE>-1</CODE> is returned.
1031 *
1032 * @param uri the uri of the resource to get the last modified time of. Must
1033 * not be null.
1034 * @return The last modified time of the resource pointed to by the
1035 * specified {@link URI} in milliseconds since the epoch.
1036 *
1037 * @see VirtualFileSystemHelper#lastModified( URI )
1038 */
1039 public long lastModified( URI uri )
1040 {
1041 if ( uri == null )
1042 {
1043 throw new NullPointerException( "uri must not be null" );
1044 }
1045 return findHelper( uri ).lastModified( uri );
1046 }
1047
1048
1049 /**
1050 * Returns an array of {@link URI}s identifying resources in
1051 * the directory resource indicated by the {@link URI}. If the specified
1052 * {@link URI} does not represent a directory, then this method
1053 * returns <CODE>null</CODE>. Otherwise, an array of {@link URI}s
1054 * is returned, one for each file or directory in the directory.
1055 * {@link URI}s representing the directory itself or its parent are
1056 * not included in the result. There is no guarantee that the
1057 * {@link URI}s will be returned in any particular order.<P>
1058 *
1059 * @param uri the uri of a directory resource. Must not be null.
1060 * @return An array of {@link URI}s naming the files and directories
1061 * in the directory indicated by the {@link URI}. The array will
1062 * be empty if the directory is empty. Returns <CODE>null</CODE>
1063 * if the {@link URI} does not represent a directory or if an
1064 * I/O error occurs.
1065 *
1066 * @see VirtualFileSystemHelper#list( URI )
1067 */
1068 public URI[] list( URI uri )
1069 {
1070 if ( uri == null )
1071 {
1072 throw new NullPointerException( "uri must not be null" );
1073 }
1074 return findHelper( uri ).list( uri );
1075 }
1076
1077
1078 /**
1079 * Returns an array of {@link URI}s identifying resources in
1080 * the directory resource indicated by the {@link URI}; the specified
1081 * {@link URIFilter} is applied to determine which {@link URI}s will
1082 * be returned. If the specified {@link URI} does not represent a
1083 * directory, then this method returns <CODE>null</CODE>.
1084 * Otherwise, an array of {@link URI}s is returned, one for each file
1085 * or directory in the directory that is accepted by the specified
1086 * filter. {@link URI}s representing the directory itself or its
1087 * parent are not included in the result. There is no guarantee that
1088 * the {@link URI}s will occur in any particular order.<P>
1089 *
1090 * If the specified {@link URIFilter} is <CODE>null</CODE> then
1091 * no filtering behavior is done.
1092 *
1093 * @param uri the uri of a directory resource. Must not be null.
1094 * @param filter a filter to use when retrieving the child resources
1095 * of the specified uri. May be null, in which case no filtering is done.
1096 * @return An array of {@link URI}s naming the files and directories
1097 * in the directory indicated by the {@link URI} that are accepted
1098 * by the specified {@link URIFilter}. The array will be empty if
1099 * the directory is empty. Returns <CODE>null</CODE> if the
1100 * {@link URI} does not represent a directory or if an I/O error
1101 * occurs.
1102 *
1103 * @see VirtualFileSystemHelper#list( URI, URIFilter )
1104 */
1105 public URI[] list( URI uri, URIFilter filter )
1106 {
1107 if ( uri == null )
1108 {
1109 throw new NullPointerException( "uri must not be null" );
1110 }
1111 return findHelper( uri ).list( uri, filter );
1112 }
1113
1114 /**
1115 * Returns an array of {@link URI}s that represent the root resources
1116 * available. The determination of the roots is delegated to each
1117 * registered {@link VirtualFileSystemHelper}.
1118 *
1119 * @return an array of root resources retrieved from each registered
1120 * {@link VirtualFileSystemHelper}.
1121 */
1122 public URI[] listRoots()
1123 {
1124 final ArrayList roots = new ArrayList( 40 );
1125 final ArrayList helperKeys = new ArrayList( _helpers.keySet() );
1126 Collections.sort( helperKeys );
1127 for ( Iterator iter = helperKeys.iterator(); iter.hasNext(); )
1128 {
1129 final String scheme = iter.next().toString();
1130 final VirtualFileSystemHelper helper =
1131 (VirtualFileSystemHelper) _helpers.get( scheme );
1132 final URI[] schemeRoots = helper.listRoots();
1133 if ( schemeRoots != null )
1134 {
1135 roots.addAll( Arrays.asList( schemeRoots ) );
1136 }
1137 }
1138 return (URI[]) roots.toArray( new URI[ roots.size() ] );
1139 }
1140
1141 /**
1142 * Creates the directory indicated by the {@link URI}.<P>
1143 *
1144 * @param uri the uri of a potential directory resource. Must not be null.
1145 * @return <CODE>true</CODE> if and only if the directory was
1146 * created; <CODE>false</CODE> otherwise.
1147 * @see VirtualFileSystemHelper#mkdir( URI )
1148 */
1149 public boolean mkdir( URI uri )
1150 {
1151 if ( uri == null )
1152 {
1153 throw new NullPointerException( "uri must not be null" );
1154 }
1155 return findHelper( uri ).mkdir( uri );
1156 }
1157
1158
1159 /**
1160 * Creates the directory indicated by the specified {@link URI}
1161 * including any necessary but nonexistent parent directories. Note
1162 * that if this operation fails, it may have succeeded in creating
1163 * some of the necessary parent directories. This method returns
1164 * <CODE>true</CODE> if the directory was created along with all
1165 * necessary parent directories or if the directories already
1166 * exist; it returns <CODE>false</CODE> otherwise.
1167 *
1168 * @param uri the uri of a potential directory resource. Must not be null.
1169 * @return <CODE>true</CODE> if all directories were created
1170 * successfully or exist; <CODE>false</CODE> if there was a
1171 * failure somewhere.
1172 * Note that even if <CODE>false</CODE> is returned, some directories
1173 * may still have been created.
1174 * @see VirtualFileSystemHelper#mkdirs( URI )
1175 */
1176 public boolean mkdirs( URI uri )
1177 {
1178 if ( uri == null )
1179 {
1180 throw new NullPointerException( "uri must not be null" );
1181 }
1182
1183 return findHelper( uri ).mkdirs( uri );
1184 }
1185
1186 /**
1187 * Creates a new empty temporary file in the specified directory using the
1188 * given prefix and suffix strings to generate its name.
1189 *
1190 * @param prefix The prefix string to be used in generating the file's name;
1191 * must be at least three characters long
1192 *
1193 * @param suffix The directory in which the file is to be created, or
1194 * null if the default temporary-file directory is to be used
1195 *
1196 * @param directory The directory in which the file is to be created,
1197 * or null if the default temporary-file directory is to be used
1198 *
1199 * @return The <CODE>URI</CODE> to the temporary file.
1200 * @throws java.io.IOException if an error occurs creating the temporary
1201 * file.
1202 *
1203 * @see VirtualFileSystemHelper#createTempFile( String, String, URI )
1204 */
1205 public URI createTempFile( String prefix,
1206 String suffix,
1207 URI directory ) throws IOException
1208 {
1209 if ( prefix == null || prefix.length() < 3 )
1210 {
1211 throw new IllegalArgumentException(
1212 "prefix must be at least three characters long" );
1213 }
1214
1215 return ( directory != null
1216 ? findHelper( directory ).createTempFile( prefix, suffix, directory )
1217 : findHelper( FILE_SCHEME ).createTempFile( prefix, suffix, null ) );
1218 }
1219
1220
1221 /**
1222 * Opens an {@link InputStream} for the location indicated by the
1223 * specified {@link URI}.
1224 *
1225 * @param uri An {@link InputStream} is opened on the given
1226 * {@link URI}. Must not be null.
1227 *
1228 * @return The {@link InputStream} associated with the {@link URI}.
1229 *
1230 * @exception java.io.FileNotFoundException if the resource at the
1231 * specified URI does not exist.
1232 *
1233 * @exception IOException if an I/O error occurs when trying to open
1234 * the {@link InputStream}.
1235 *
1236 * @exception java.net.UnknownServiceException (a runtime exception) if
1237 * the scheme does not support opening an {@link InputStream}.
1238 *
1239 * @see VirtualFileSystemHelper#openInputStream( URI )
1240 */
1241 public InputStream openInputStream( URI uri ) throws IOException
1242 {
1243 if ( uri == null )
1244 {
1245 throw new NullPointerException( "uri must not be null" );
1246 }
1247 return findHelper( uri ).openInputStream( uri );
1248 }
1249
1250
1251 /**
1252 * Opens an {@link OutputStream} on the {@link URI}. If the file
1253 * does not exist, the file should be created. If the directory
1254 * path to the file does not exist, all necessary directories
1255 * should be created.
1256 *
1257 * @param uri An {@link OutputStream} is opened on the given
1258 * {@link URI}. Must not be null.
1259 *
1260 * @return The {@link OutputStream} associated with the {@link URI}.
1261 *
1262 * @exception IOException if an I/O error occurs when trying to open
1263 * the {@link OutputStream}.
1264 *
1265 * @exception java.net.UnknownServiceException (a runtime exception)
1266 * if the scheme does not support opening an {@link OutputStream}.
1267 *
1268 * @see VirtualFileSystemHelper#openOutputStream( URI )
1269 */
1270 public OutputStream openOutputStream( URI uri ) throws IOException
1271 {
1272 if ( uri == null )
1273 {
1274 throw new NullPointerException( "uri must not be null" );
1275 }
1276 return findHelper( uri ).openOutputStream( uri );
1277 }
1278
1279
1280 /**
1281 * Renames the resource indicated by the first {@link URI} to the
1282 * name indicated by the second {@link URI}.<P>
1283 *
1284 * If either {@link URI} parameter is <CODE>null</CODE> or if both
1285 * of the specified {@link URI} parameters refer to the same
1286 * resource, then the rename is not attempted and failure is
1287 * returned.<P>
1288 *
1289 * If the specified {@link URI} parameters do not have the same
1290 * scheme, then the <CODE>VirtualFileSystem</CODE> handles the rename
1291 * by first copying the resource to the destination with {@link
1292 * VirtualFileSystem#copy(URI, URI)} and then deleting the original
1293 * resource with {@link VirtualFileSystem#delete(URI)}; if either
1294 * operation fails, then failure is returned.<P>
1295 *
1296 * Otherwise, the scheme helper is called to perform the actual
1297 * rename operation. Scheme helper implementations may therefore
1298 * assume that both {@link URI} parameters are not
1299 * <CODE>null</CODE>, do not refer to the same resource, and have
1300 * the same scheme.<P>
1301 *
1302 * If the original {@link URI} refers to a nonexistent resource,
1303 * then the scheme helper implementations should return failure.
1304 * It is left up to the scheme helper implementations to decide
1305 * whether to overwrite the destination or return failure if the
1306 * destination {@link URI} refers to an existing resource.
1307 *
1308 * @param oldURI the {@link URI} of the original resource
1309 * @param newURI the desired {@link URI} for the renamed resource
1310 *
1311 * @return <CODE>true</CODE> if and only if the resource is
1312 * successfully renamed; <CODE>false</CODE> otherwise.
1313 */
1314 public boolean renameTo( URI oldURI, URI newURI )
1315 {
1316 if ( oldURI == null || newURI == null )
1317 {
1318 return false;
1319 }
1320 else if ( oldURI == newURI || oldURI.equals( newURI ) )
1321 {
1322 return false;
1323 }
1324 else if ( !schemesAreEqual( oldURI, newURI ) )
1325 {
1326 try
1327 {
1328 copy( oldURI, newURI );
1329 }
1330 catch ( IOException e )
1331 {
1332 return false;
1333 }
1334
1335 return delete( oldURI );
1336 }
1337 return findHelper( oldURI ).renameTo( oldURI, newURI );
1338 }
1339
1340
1341 /**
1342 * Sets the last-modified timestamp of the resource indicated by
1343 * the {@link URI} to the time specified by <CODE>time</CODE>.
1344 * The time is specified in the number of milliseconds since
1345 * the epoch (00:00:00 GMT Jan 1, 1970). The return value
1346 * indicates whether or not the setting of the timestamp
1347 * succeeded.
1348 *
1349 * @param uri the uri of the resource to set the last modified timesamp of.
1350 * Must not be null.
1351 * @param time a last modified timestamp in milliseconds since the epoch.
1352 *
1353 * @return true if the last modified time of the specified resource was
1354 * successful.
1355 *
1356 * @see VirtualFileSystemHelper#setLastModified( URI, long )
1357 */
1358 public boolean setLastModified( URI uri, long time )
1359 {
1360 if ( uri == null )
1361 {
1362 throw new NullPointerException( "uri must not be null" );
1363 }
1364
1365 return findHelper( uri ).setLastModified( uri, time );
1366 }
1367
1368
1369 /**
1370 * Sets the read-only status of the resource indicated by the
1371 * {@link URI} according to the specified <CODE>readOnly</CODE>
1372 * flag. The return value indicates whether or not the setting
1373 * of the read-only flag succeeded.
1374 *
1375 * @param uri the uri of a resource to set the read only flag of. Must not
1376 * be null.
1377 * @param readOnly whether the specified resource should be read only.
1378 *
1379 * @return true if the read only flag on the specified resource was
1380 * successfully set.
1381 *
1382 * @see VirtualFileSystemHelper#setReadOnly( URI, boolean )
1383 */
1384 public boolean setReadOnly( URI uri, boolean readOnly )
1385 {
1386 if ( uri == null )
1387 {
1388 throw new NullPointerException( "uri must not be null" );
1389 }
1390
1391 return findHelper( uri ).setReadOnly( uri, readOnly );
1392 }
1393
1394
1395 /**
1396 * Returns a displayable form of the complete {@link URI}.
1397 *
1398 * @param uri the uri of a resource. Must not be null.
1399 * @return a human-readable string representing the resource.
1400 *
1401 * @see VirtualFileSystemHelper#toDisplayString( URI )
1402 */
1403 public String toDisplayString( URI uri )
1404 {
1405 if ( uri == null )
1406 {
1407 throw new NullPointerException( "uri must not be null" );
1408 }
1409
1410 return findHelper( uri ).toDisplayString( uri );
1411 }
1412
1413
1414 /**
1415 * Converts a uri to a relative spec.
1416 *
1417 * @param uri the uri to convert. Must not be null.
1418 * @param base the base uri.
1419 * @return the relative spec of <tt>uri</tt>
1420 * @see VirtualFileSystemHelper#toRelativeSpec( URI, URI )
1421 */
1422 public String toRelativeSpec( URI uri, URI base )
1423 {
1424 if ( uri == null )
1425 {
1426 throw new NullPointerException( "uri must not be null" );
1427 }
1428 if ( base == null )
1429 {
1430 throw new NullPointerException( "base must not be null" );
1431 }
1432 return findHelper( uri ).toRelativeSpec( uri, base );
1433 }
1434
1435
1436 /**
1437 * Converts a uri to a relative spec.
1438 *
1439 * @param uri the uri to convert. Must not be null.
1440 * @param base the base uri.
1441 * @param mustConsumeBase If <CODE>mustConsumeBase</CODE> is
1442 * <CODE>false</CODE>, then
1443 * this method will return a non-<CODE>null</CODE> relative
1444 * spec regardless of how much of the base {@link URI} is
1445 * consumed during the determination.
1446 * @return the relative spec of <tt>uri</tt>
1447 * @see VirtualFileSystemHelper#toRelativeSpec( URI, URI )
1448 */
1449 public String toRelativeSpec( URI uri, URI base, boolean mustConsumeBase )
1450 {
1451 return findHelper( uri ).toRelativeSpec( uri, base, mustConsumeBase );
1452 }
1453
1454
1455 /**
1456 * This method gets the base directory fully containing the relative path.
1457 *
1458 * @param uri the uri to get the base directory for.
1459 * @param relativeSpec a relative path.
1460 * @return the base directory fully containing the relative path.
1461 * @see VirtualFileSystemHelper#getBaseParent(URI, String)
1462 */
1463 public URI getBaseParent( URI uri, String relativeSpec )
1464 {
1465 return findHelper( uri ).getBaseParent( uri, relativeSpec );
1466 }
1467
1468 /**
1469 * Get a {@link URL} from a {@link URI}.
1470 *
1471 * @param uri the URI to convert to a URL.
1472 * @return a url representation of the uri.
1473 *
1474 * @throws MalformedURLException if the uri could not be converted into
1475 * a URL.
1476 */
1477 public URL toURL( URI uri ) throws MalformedURLException
1478 {
1479 return findHelper( uri ).toURL( uri );
1480 }
1481 //--------------------------------------------------------------------------
1482 // implementation details...
1483 //--------------------------------------------------------------------------
1484 /**
1485 * Compares the schemes of the specified {@link URI} parameters
1486 * for equality. The schemes are considered to be unequal if
1487 * either {@link URI} is <CODE>null</CODE>, even when both {@link
1488 * URI} parameters are <CODE>null</CODE>.
1489 */
1490 private static boolean schemesAreEqual( URI uri1, URI uri2 )
1491 {
1492 if ( uri1 == null || uri2 == null )
1493 {
1494 return false;
1495 }
1496 final String p1 = uri1.getScheme();
1497 final String p2 = uri2.getScheme();
1498 return p1.equals( p2 );
1499 }
1500
1501 /**
1502 * Gets the VirtualFileSystem implementation for this IDE.
1503 *
1504 * @return the virtual file system implementation for this ide.
1505 */
1506 public static VirtualFileSystem getVirtualFileSystem()
1507 {
1508 try
1509 {
1510 return (VirtualFileSystem) getService( VirtualFileSystem.class );
1511 }
1512 catch ( ProviderNotFoundException lnfe )
1513 {
1514 lnfe.printStackTrace();
1515 throw new IllegalStateException( "No virtual file system" );
1516 }
1517 }
1518
1519 public VirtualFileSystem(){}
1520}