View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.impl;
18  
19  import java.io.File;
20  import java.lang.reflect.Constructor;
21  import java.net.URI;
22  import java.net.URISyntaxException;
23  import java.net.URL;
24  import java.net.URLStreamHandler;
25  import java.net.URLStreamHandlerFactory;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.commons.vfs2.CacheStrategy;
35  import org.apache.commons.vfs2.Capability;
36  import org.apache.commons.vfs2.FileContentInfoFactory;
37  import org.apache.commons.vfs2.FileName;
38  import org.apache.commons.vfs2.FileObject;
39  import org.apache.commons.vfs2.FileSystem;
40  import org.apache.commons.vfs2.FileSystemConfigBuilder;
41  import org.apache.commons.vfs2.FileSystemException;
42  import org.apache.commons.vfs2.FileSystemManager;
43  import org.apache.commons.vfs2.FileSystemOptions;
44  import org.apache.commons.vfs2.FileType;
45  import org.apache.commons.vfs2.FilesCache;
46  import org.apache.commons.vfs2.NameScope;
47  import org.apache.commons.vfs2.VFS;
48  import org.apache.commons.vfs2.cache.SoftRefFilesCache;
49  import org.apache.commons.vfs2.operations.FileOperationProvider;
50  import org.apache.commons.vfs2.provider.AbstractFileName;
51  import org.apache.commons.vfs2.provider.AbstractFileProvider;
52  import org.apache.commons.vfs2.provider.DefaultURLStreamHandler;
53  import org.apache.commons.vfs2.provider.FileProvider;
54  import org.apache.commons.vfs2.provider.FileReplicator;
55  import org.apache.commons.vfs2.provider.LocalFileProvider;
56  import org.apache.commons.vfs2.provider.TemporaryFileStore;
57  import org.apache.commons.vfs2.provider.UriParser;
58  import org.apache.commons.vfs2.provider.VfsComponent;
59  
60  /**
61   * The default file system manager implementation.
62   */
63  public class DefaultFileSystemManager implements FileSystemManager
64  {
65      /**
66       * Mapping from URI scheme to FileProvider.
67       */
68      private final Map<String, FileProvider> providers = new HashMap<String, FileProvider>();
69  
70      /**
71       * All components used by this manager.
72       */
73      private final ArrayList<Object> components = new ArrayList<Object>();
74  
75      /**
76       * The context to pass to providers.
77       */
78      private final DefaultVfsComponentContext context = new DefaultVfsComponentContext(this);
79  
80      /**
81       * Operations providers added to this manager.
82       */
83      private final Map<String, List<FileOperationProvider>> operationProviders =
84              new HashMap<String, List<FileOperationProvider>>();
85  
86      /**
87       * Mappings of file types.
88       */
89      private final FileTypeMap typeMap = new FileTypeMap();
90  
91  
92      /**
93       * The provider for local files.
94       */
95      private LocalFileProvider localFileProvider;
96  
97      /**
98       * The default provider.
99       */
100     private FileProvider defaultProvider;
101 
102     /**
103      * The file replicator to use.
104      */
105     private FileReplicator fileReplicator;
106 
107     /**
108      * The base file to use for relative URI.
109      */
110     private FileObject baseFile;
111 
112     /**
113      * The files cache
114      */
115     private FilesCache filesCache;
116 
117     /**
118      * The cache strategy
119      */
120     private CacheStrategy fileCacheStrategy;
121 
122     /**
123      * Class which decorates all returned fileObjects
124      */
125     private Class<?> fileObjectDecorator;
126     /**
127      * Reflection constructor extracted from {@link #fileObjectDecorator}
128      */
129     private Constructor<?> fileObjectDecoratorConst;
130 
131     /**
132      * The class to use to determine the content-type (mime-type)
133      */
134     private FileContentInfoFactory fileContentInfoFactory;
135 
136     /**
137      * The logger to use. Default implementation.
138      */
139     private Log log = LogFactory.getLog(getClass());
140 
141     /**
142      * The temporary file store to use.
143      */
144     private TemporaryFileStore tempFileStore;
145 
146     /**
147      * The virtual file provider.
148      */
149     private VirtualFileProvider vfsProvider;
150 
151     /**
152      * Flag, if manager is initialized (after init() and before close()).
153      */
154     private boolean init;
155 
156 
157     /**
158      * Returns the logger used by this manager.
159      * @return the Logger.
160      */
161     protected Log getLogger()
162     {
163         return log;
164     }
165 
166     /**
167      * Registers a file system provider.
168      * <p>
169      * The manager takes care of all lifecycle management.
170      * A provider may be registered multiple times.
171      * The first {@link LocalFileProvider} added will be
172      * remembered for {@link #getLocalFileProvider()}.
173      *
174      * @param urlScheme
175      *            The scheme the provider will handle.
176      * @param provider
177      *            The provider.
178      * @throws FileSystemException if an error occurs adding the provider.
179      */
180     public void addProvider(final String urlScheme, final FileProvider provider)
181             throws FileSystemException
182     {
183         addProvider(new String[] {urlScheme}, provider);
184     }
185 
186     /**
187      * Registers a file system provider.
188      * <p>
189      * The manager takes care of all lifecycle management.
190      * A provider may be registered multiple times.
191      * The first {@link LocalFileProvider} added will be
192      * remembered for {@link #getLocalFileProvider()}.
193      *
194      * @param urlSchemes
195      *            The schemes the provider will handle.
196      * @param provider
197      *            The provider.
198      * @throws FileSystemException if an error occurs adding the provider.
199      */
200     public void addProvider(final String[] urlSchemes,
201             final FileProvider provider) throws FileSystemException
202     {
203         // fail duplicate schemes
204         for (final String scheme : urlSchemes)
205         {
206             if (providers.containsKey(scheme))
207             {
208                 throw new FileSystemException(
209                         "vfs.impl/multiple-providers-for-scheme.error", scheme);
210             }
211         }
212 
213         // Contextualise the component (if not already)
214         setupComponent(provider);
215 
216         // Add to map
217         for (final String scheme : urlSchemes)
218         {
219             providers.put(scheme, provider);
220         }
221 
222         if (provider instanceof LocalFileProvider && localFileProvider == null)
223         {
224             localFileProvider = (LocalFileProvider) provider;
225         }
226     }
227 
228     /**
229      * Returns true if this manager has a provider for a particular scheme.
230      *
231      * @param scheme The scheme to check.
232      * @return true if a provider is configured for this scheme, false otherwise.
233      */
234     @Override
235     public boolean hasProvider(final String scheme)
236     {
237         return providers.containsKey(scheme);
238     }
239 
240     /**
241      * Adds an filename extension mapping.
242      *
243      * @param extension The file name extension.
244      * @param scheme The scheme to use for files with this extension.
245      */
246     public void addExtensionMap(final String extension, final String scheme)
247     {
248         typeMap.addExtension(extension, scheme);
249     }
250 
251     /**
252      * Adds a mime type mapping.
253      *
254      * @param mimeType The mime type.
255      * @param scheme The scheme to use for files with this mime type.
256      */
257     public void addMimeTypeMap(final String mimeType, final String scheme)
258     {
259         typeMap.addMimeType(mimeType, scheme);
260     }
261 
262     /**
263      * Sets the default provider. This is the provider that will handle URI with
264      * unknown schemes. The manager takes care of all lifecycle management.
265      * @param provider The FileProvider.
266      * @throws FileSystemException if an error occurs setting the provider.
267      */
268     public void setDefaultProvider(final FileProvider provider)
269             throws FileSystemException
270     {
271         setupComponent(provider);
272         defaultProvider = provider;
273     }
274 
275     /**
276      * Returns the filesCache implementation used to cache files.
277      * @return The FilesCache.
278      */
279     @Override
280     public FilesCache getFilesCache()
281     {
282         return filesCache;
283     }
284 
285     /**
286      * Sets the filesCache implementation used to cache files.
287      * <p>
288      * Can only be set before the FileSystemManager is initialized.
289      * <p>
290      * The manager takes care of the lifecycle. If none is set, a default is picked
291      * in {@link #init()}.
292      *
293      * @param filesCache The FilesCache.
294      * @throws FileSystemException if an error occurs setting the cache..
295      */
296     public void setFilesCache(final FilesCache filesCache)
297             throws FileSystemException
298     {
299         if (init)
300         {
301             throw new FileSystemException("vfs.impl/already-inited.error");
302         }
303 
304         this.filesCache = filesCache;
305     }
306 
307     /**
308      * Set the cache strategy to use when dealing with file object data.
309      * <p>
310      * Can only be set before the FileSystemManager is initialized.
311      * <p>
312      * The default is {@link CacheStrategy#ON_RESOLVE}
313      *
314      * @param fileCacheStrategy The CacheStrategy to use.
315      * @throws FileSystemException
316      *             if this is not possible. e.g. it is already set.
317      */
318     public void setCacheStrategy(final CacheStrategy fileCacheStrategy)
319             throws FileSystemException
320     {
321         if (init)
322         {
323             throw new FileSystemException("vfs.impl/already-inited.error");
324         }
325 
326         this.fileCacheStrategy = fileCacheStrategy;
327     }
328 
329     /**
330      * Get the cache strategy used.
331      * @return The CacheStrategy.
332      */
333     @Override
334     public CacheStrategy getCacheStrategy()
335     {
336         return fileCacheStrategy;
337     }
338 
339     /**
340      * Get the file object decorator used.
341      * @return The decorator.
342      */
343     @Override
344     public Class<?> getFileObjectDecorator()
345     {
346         return fileObjectDecorator;
347     }
348 
349     /**
350      * The constructor associated to the fileObjectDecorator.
351      * We cache it here for performance reasons.
352      * @return The decorator's Constructor.
353      */
354     @Override
355     public Constructor<?> getFileObjectDecoratorConst()
356     {
357         return fileObjectDecoratorConst;
358     }
359 
360     /**
361      * Set a fileObject decorator to be used for ALL returned file objects.
362      * <p>
363      * Can only be set before the FileSystemManager is initialized.
364      *
365      * @param fileObjectDecorator must be inherted from {@link DecoratedFileObject} a has to provide a
366      * constructor with a single {@link FileObject} as argument
367      * @throws FileSystemException if an error occurs setting the decorator.
368      */
369     public void setFileObjectDecorator(final Class<?> fileObjectDecorator) throws FileSystemException
370     {
371         if (init)
372         {
373             throw new FileSystemException("vfs.impl/already-inited.error");
374         }
375         if (!DecoratedFileObject.class.isAssignableFrom(fileObjectDecorator))
376         {
377             throw new FileSystemException("vfs.impl/invalid-decorator.error", fileObjectDecorator.getName());
378         }
379 
380         try
381         {
382             fileObjectDecoratorConst = fileObjectDecorator.getConstructor(new Class[]{FileObject.class});
383         }
384         catch (final NoSuchMethodException e)
385         {
386             throw new FileSystemException("vfs.impl/invalid-decorator.error", fileObjectDecorator.getName(), e);
387         }
388 
389         this.fileObjectDecorator = fileObjectDecorator;
390     }
391 
392     /**
393      * get the fileContentInfoFactory used to determine the infos of a file
394      * content.
395      * @return The FileContentInfoFactory.
396      */
397     @Override
398     public FileContentInfoFactory getFileContentInfoFactory()
399     {
400         return fileContentInfoFactory;
401     }
402 
403     /**
404      * set the fileContentInfoFactory used to determine the infos of a file
405      * content.
406      * <p>
407      * Can only be set before the FileSystemManager is initialized.
408      *
409      * @param fileContentInfoFactory The FileContentInfoFactory.
410      * @throws FileSystemException if an error occurs setting the FileContentInfoFactory.
411      */
412     public void setFileContentInfoFactory(final FileContentInfoFactory fileContentInfoFactory)
413             throws FileSystemException
414     {
415         if (init)
416         {
417             throw new FileSystemException("vfs.impl/already-inited.error");
418         }
419 
420         this.fileContentInfoFactory = fileContentInfoFactory;
421     }
422 
423     /**
424      * Sets the file replicator to use.
425      * <p>
426      * The manager takes care of all lifecycle management.
427      *
428      * @param replicator The FileReplicator.
429      * @throws FileSystemException if an error occurs setting the replicator.
430      */
431     public void setReplicator(final FileReplicator replicator)
432             throws FileSystemException
433     {
434         setupComponent(replicator);
435         fileReplicator = replicator;
436     }
437 
438     /**
439      * Sets the temporary file store to use.
440      * <p>
441      * The manager takes care of all lifecycle management.
442      *
443      * @param tempFileStore The temporary FileStore.
444      * @throws FileSystemException if an error occurs adding the file store.
445      */
446     public void setTemporaryFileStore(final TemporaryFileStore tempFileStore)
447             throws FileSystemException
448     {
449         setupComponent(tempFileStore);
450         this.tempFileStore = tempFileStore;
451     }
452 
453     /**
454      * Sets the logger to use.
455      * <p>
456      * This overwrites the default logger for this manager and is not reset in {@link #close()}.
457      *
458      * @param log The Logger to use.
459      */
460     @Override
461     public void setLogger(final Log log)
462     {
463         this.log = log;
464     }
465 
466     /**
467      * Initializes a component, if it has not already been initialised.
468      *
469      * @param component The component to setup.
470      * @throws FileSystemException if an error occurs.
471      */
472     private void setupComponent(final Object component)
473             throws FileSystemException
474     {
475         if (!components.contains(component))
476         {
477             if (component instanceof VfsComponent)
478             {
479                 final VfsComponent vfsComponent = (VfsComponent) component;
480                 vfsComponent.setLogger(getLogger());
481                 vfsComponent.setContext(context);
482                 vfsComponent.init();
483             }
484             components.add(component);
485         }
486     }
487 
488     /**
489      * Closes a component, if it has not already been closed.
490      *
491      * @param component The component to close.
492      */
493     private void closeComponent(final Object component)
494     {
495         if (component != null && components.contains(component))
496         {
497             if (component instanceof VfsComponent)
498             {
499                 final VfsComponent vfsComponent = (VfsComponent) component;
500                 vfsComponent.close();
501             }
502             components.remove(component);
503         }
504     }
505 
506     /**
507      * Returns the file replicator.
508      *
509      * @return The file replicator. Never returns null.
510      * @throws FileSystemException if there is no FileReplicator.
511      */
512     public FileReplicator getReplicator() throws FileSystemException
513     {
514         if (fileReplicator == null)
515         {
516             throw new FileSystemException("vfs.impl/no-replicator.error");
517         }
518         return fileReplicator;
519     }
520 
521     /**
522      * Returns the temporary file store.
523      *
524      * @return The file store. Never returns null.
525      * @throws FileSystemException if there is no TemporaryFileStore.
526      */
527     public TemporaryFileStore getTemporaryFileStore()
528             throws FileSystemException
529     {
530         if (tempFileStore == null)
531         {
532             throw new FileSystemException("vfs.impl/no-temp-file-store.error");
533         }
534         return tempFileStore;
535     }
536 
537     /**
538      * Initializes this manager.
539      * <p>
540      * If no value for the following properties was specified, it will
541      * use the following defaults:
542      * <ul>
543      * <li>fileContentInfoFactory = new FileContentInfoFilenameFactory()</li>
544      * <li>filesCache = new SoftRefFilesCache()</li>
545      * <li>fileCacheStrategy = CacheStrategy.ON_RESOLVE</li>
546      * </ul>
547      *
548      * @throws FileSystemException if an error occurs during initialization.
549      */
550     public void init() throws FileSystemException
551     {
552         if (fileContentInfoFactory == null)
553         {
554             fileContentInfoFactory = new FileContentInfoFilenameFactory();
555         }
556 
557         if (filesCache == null)
558         {
559             // filesCache = new DefaultFilesCache();
560             filesCache = new SoftRefFilesCache();
561         }
562         if (fileCacheStrategy == null)
563         {
564             fileCacheStrategy = CacheStrategy.ON_RESOLVE;
565         }
566         setupComponent(filesCache);
567 
568         vfsProvider = new VirtualFileProvider();
569         setupComponent(vfsProvider);
570 
571         init = true;
572     }
573 
574     /**
575      * Closes the manager.
576      * <p>
577      * This will close all providers (all files), it will also close
578      * all managed components including temporary files, replicator,
579      * file cache and file operations.
580      * <p>
581      * The manager is in uninitialized state after this method.
582      */
583     public void close()
584     {
585         if (!init)
586         {
587             return;
588         }
589 
590         // make sure all discovered components in
591         // org.apache.commons.vfs2.impl.StandardFileSystemManager.configure(Element)
592         // are closed here
593 
594         // Close the file system providers.
595         for (final FileProvider provider : providers.values())
596         {
597             closeComponent(provider);
598         }
599         // unregister all
600         providers.clear();
601 
602         // Close the other components
603         closeComponent(vfsProvider);
604         closeComponent(fileReplicator);
605         closeComponent(tempFileStore);
606         closeComponent(filesCache);
607         closeComponent(defaultProvider);
608 
609         // FileOperations are components, too
610         for (final List<FileOperationProvider> opproviders : operationProviders.values())
611         {
612             for (final FileOperationProvider p : opproviders)
613             {
614                 closeComponent(p);
615             }
616         }
617         // unregister all
618         operationProviders.clear();
619 
620         // collections with add()
621         typeMap.clear();
622 
623         // should not happen, but make debugging easier:
624         if (!components.isEmpty())
625         {
626             log.warn("DefaultFilesystemManager.close: not all components are closed: " + components.toString());
627         }
628         components.clear();
629 
630         // managed components
631         vfsProvider = null;
632 
633         // setters and derived state
634         defaultProvider = null;
635         baseFile = null;
636         fileObjectDecorator = null;
637         fileObjectDecoratorConst = null;
638         localFileProvider = null;
639         fileReplicator = null;
640         tempFileStore = null;
641         // setters with init() defaults
642         filesCache = null;
643         fileCacheStrategy = null;
644         fileContentInfoFactory = null;
645 
646         init = false;
647     }
648 
649     /**
650      * Free all resources used by unused filesystems created by this manager.
651      */
652     public void freeUnusedResources()
653     {
654         if (!init)
655         {
656             return;
657         }
658 
659         // Close the providers.
660         for (final FileProvider fileProvider : providers.values())
661         {
662             final AbstractFileProvider provider = (AbstractFileProvider) fileProvider;
663             provider.freeUnusedResources();
664         }
665         // vfsProvider does not need to free resources
666     }
667 
668     /**
669      * Sets the base file to use when resolving relative URI.
670      * @param baseFile The new base FileObject.
671      * @throws FileSystemException if an error occurs.
672      */
673     public void setBaseFile(final FileObject baseFile)
674             throws FileSystemException
675     {
676         this.baseFile = baseFile;
677     }
678 
679     /**
680      * Sets the base file to use when resolving relative URI.
681      * @param baseFile The new base FileObject.
682      * @throws FileSystemException if an error occurs.
683      */
684     public void setBaseFile(final File baseFile) throws FileSystemException
685     {
686         this.baseFile = getLocalFileProvider().findLocalFile(baseFile);
687     }
688 
689     /**
690      * Returns the base file used to resolve relative URI.
691      * @return The FileObject that represents the base file.
692      * @throws FileSystemException if an error occurs.
693      */
694     @Override
695     public FileObject getBaseFile() throws FileSystemException
696     {
697         return baseFile;
698     }
699 
700     /**
701      * Locates a file by URI.
702      * @param uri The URI of the file to locate.
703      * @return The FileObject for the located file.
704      * @throws FileSystemException if the file cannot be located or an error occurs.
705      */
706     @Override
707     public FileObject resolveFile(final String uri) throws FileSystemException
708     {
709         return resolveFile(getBaseFile(), uri);
710     }
711 
712     /**
713      * Locate a file by URI, use the FileSystemOptions for file-system creation.
714      * @param uri The URI of the file to locate.
715      * @param fileSystemOptions The options for the FileSystem.
716      * @return The FileObject for the located file.
717      * @throws FileSystemException if the file cannot be located or an error occurs.
718      */
719 
720     @Override
721     public FileObject resolveFile(final String uri,
722             final FileSystemOptions fileSystemOptions)
723             throws FileSystemException
724     {
725         // return resolveFile(baseFile, uri, fileSystemOptions);
726         return resolveFile(getBaseFile(), uri, fileSystemOptions);
727     }
728 
729     /**
730      * Resolves a URI, relative to base file.
731      * <p>
732      * Uses the {@linkplain #getLocalFileProvider() local file provider} to
733      * locate the system file.
734      *
735      * @param baseFile The base File to use to locate the file.
736      * @param uri The URI of the file to locate.
737      * @return The FileObject for the located file.
738      * @throws FileSystemException if the file cannot be located or an error occurs.
739      */
740     @Override
741     public FileObject resolveFile(final File baseFile, final String uri)
742             throws FileSystemException
743     {
744         final FileObject baseFileObj = getLocalFileProvider().findLocalFile(
745                 baseFile);
746         return resolveFile(baseFileObj, uri);
747     }
748 
749     /**
750      * Resolves a URI, relative to a base file.
751      * @param baseFile The base FileOjbect to use to locate the file.
752      * @param uri The URI of the file to locate.
753      * @return The FileObject for the located file.
754      * @throws FileSystemException if the file cannot be located or an error occurs.
755      */
756     @Override
757     public FileObject resolveFile(final FileObject baseFile, final String uri)
758             throws FileSystemException
759     {
760         return resolveFile(baseFile, uri, baseFile == null ? null : baseFile
761                 .getFileSystem().getFileSystemOptions());
762     }
763 
764     /**
765      * Resolves a URI, relative to a base file with specified FileSystem
766      * configuration.
767      * @param baseFile The base file.
768      * @param uri The file name. May be a fully qualified or relative path or a url.
769      * @param fileSystemOptions Options to pass to the file system.
770      * @return A FileObject representing the target file.
771      * @throws FileSystemException if an error occurs accessing the file.
772      */
773     public FileObject resolveFile(final FileObject baseFile, final String uri,
774             final FileSystemOptions fileSystemOptions)
775             throws FileSystemException
776     {
777         final FileObject realBaseFile;
778         if (baseFile != null && VFS.isUriStyle()
779                 && baseFile.getName().isFile())
780         {
781             realBaseFile = baseFile.getParent();
782         }
783         else
784         {
785             realBaseFile = baseFile;
786         }
787         // TODO: use resolveName and use this name to resolve the fileObject
788 
789         UriParser.checkUriEncoding(uri);
790 
791         if (uri == null)
792         {
793             throw new IllegalArgumentException();
794         }
795 
796         // Extract the scheme
797         final String scheme = UriParser.extractScheme(uri);
798         if (scheme != null)
799         {
800             // An absolute URI - locate the provider
801             final FileProvider provider = providers.get(scheme);
802             if (provider != null)
803             {
804                 return provider.findFile(realBaseFile, uri, fileSystemOptions);
805             }
806             // Otherwise, assume a local file
807         }
808 
809         // Handle absolute file names
810         if (localFileProvider != null
811                 && localFileProvider.isAbsoluteLocalName(uri))
812         {
813             return localFileProvider.findLocalFile(uri);
814         }
815 
816         if (scheme != null)
817         {
818             // An unknown scheme - hand it to the default provider
819             if (defaultProvider == null)
820             {
821                 throw new FileSystemException("vfs.impl/unknown-scheme.error", scheme, uri);
822             }
823             return defaultProvider.findFile(realBaseFile, uri, fileSystemOptions);
824         }
825 
826         // Assume a relative name - use the supplied base file
827         if (realBaseFile == null)
828         {
829             throw new FileSystemException("vfs.impl/find-rel-file.error", uri);
830         }
831 
832         return realBaseFile.resolveFile(uri);
833     }
834 
835     /**
836      * Resolves a name, relative to the file. If the supplied name is an
837      * absolute path, then it is resolved relative to the root of the file
838      * system that the file belongs to. If a relative name is supplied, then it
839      * is resolved relative to this file name.
840      * @param root The base FileName.
841      * @param path The path to the file relative to the base FileName or an absolute path.
842      * @return The constructed FileName.
843      * @throws FileSystemException if an error occurs constructing the FileName.
844      */
845     @Override
846     public FileName resolveName(final FileName root, final String path)
847             throws FileSystemException
848     {
849         return resolveName(root, path, NameScope.FILE_SYSTEM);
850     }
851 
852     /**
853      * Resolves a name, relative to the root.
854      *
855      * @param base the base filename
856      * @param name the name
857      * @param scope the {@link NameScope}
858      * @return The FileName of the file.
859      * @throws FileSystemException if an error occurs.
860      */
861     @Override
862     public FileName resolveName(final FileName base, final String name,
863             final NameScope scope) throws FileSystemException
864     {
865         final FileName realBase;
866         if (base != null && VFS.isUriStyle() && base.isFile())
867         {
868             realBase = base.getParent();
869         }
870         else
871         {
872             realBase = base;
873         }
874 
875         final StringBuilder buffer = new StringBuilder(name);
876 
877         // Adjust separators
878         UriParser.fixSeparators(buffer);
879         String scheme = UriParser.extractScheme(buffer.toString());
880 
881         // Determine whether to prepend the base path
882         if (name.length() == 0 || (scheme == null && buffer.charAt(0) != FileName.SEPARATOR_CHAR))
883         {
884             // Supplied path is not absolute
885             if (!VFS.isUriStyle())
886             {
887                 // when using uris the parent already do have the trailing "/"
888                 buffer.insert(0, FileName.SEPARATOR_CHAR);
889             }
890             buffer.insert(0, realBase.getPath());
891         }
892 
893         // Normalise the path
894         final FileType fileType = UriParser.normalisePath(buffer);
895 
896         // Check the name is ok
897         final String resolvedPath = buffer.toString();
898         if (!AbstractFileName
899                 .checkName(realBase.getPath(), resolvedPath, scope))
900         {
901             throw new FileSystemException(
902                     "vfs.provider/invalid-descendent-name.error", name);
903         }
904 
905         String fullPath;
906         if (scheme != null)
907         {
908             fullPath = resolvedPath;
909         }
910         else
911         {
912             scheme = realBase.getScheme();
913             fullPath = realBase.getRootURI() + resolvedPath;
914         }
915         final FileProvider provider = providers.get(scheme);
916         if (provider != null)
917         {
918             // TODO: extend the filename parser to be able to parse
919             // only a pathname and take the missing informations from
920             // the base. Then we can get rid of the string operation.
921             // // String fullPath = base.getRootURI() +
922             // resolvedPath.substring(1);
923 
924             return provider.parseUri(realBase, fullPath);
925         }
926 
927         // An unknown scheme - hand it to the default provider - if possible
928         if (scheme != null && defaultProvider != null)
929         {
930             return defaultProvider.parseUri(realBase, fullPath);
931         }
932 
933         // TODO: avoid fallback to this point
934         // this happens if we have a virtual filesystem (no provider for scheme)
935         return ((AbstractFileName) realBase).createName(resolvedPath, fileType);
936     }
937 
938     /**
939      * Resolve the uri to a filename.
940      * @param uri The URI to resolve.
941      * @return The FileName of the file.
942      * @throws FileSystemException if an error occurs.
943      */
944     @Override
945     public FileName resolveURI(final String uri) throws FileSystemException
946     {
947         UriParser.checkUriEncoding(uri);
948 
949         if (uri == null)
950         {
951             throw new IllegalArgumentException();
952         }
953 
954         // Extract the scheme
955         final String scheme = UriParser.extractScheme(uri);
956         if (scheme != null)
957         {
958             // An absolute URI - locate the provider
959             final FileProvider provider = providers.get(scheme);
960             if (provider != null)
961             {
962                 return provider.parseUri(null, uri);
963             }
964 
965             // Otherwise, assume a local file
966         }
967 
968         // Handle absolute file names
969         if (localFileProvider != null
970                 && localFileProvider.isAbsoluteLocalName(uri))
971         {
972             return localFileProvider.parseUri(null, uri);
973         }
974 
975         if (scheme != null)
976         {
977             // An unknown scheme - hand it to the default provider
978             if (defaultProvider == null)
979             {
980                 throw new FileSystemException("vfs.impl/unknown-scheme.error", scheme, uri);
981             }
982             return defaultProvider.parseUri(null, uri);
983         }
984 
985         // Assume a relative name - use the supplied base file
986         if (baseFile == null)
987         {
988             throw new FileSystemException("vfs.impl/find-rel-file.error", uri);
989         }
990 
991         return resolveName(baseFile.getName(), uri, NameScope.FILE_SYSTEM);
992     }
993 
994     /**
995      * Converts a local file into a {@link FileObject}.
996      * @param file The input File.
997      * @return the create FileObject
998      * @throws FileSystemException if an error occurs creating the file.
999      */
1000     @Override
1001     public FileObject toFileObject(final File file) throws FileSystemException
1002     {
1003         return getLocalFileProvider().findLocalFile(file);
1004     }
1005 
1006     /**
1007      * Creates a layered file system.
1008      * @param scheme The scheme to use.
1009      * @param file The FileObject.
1010      * @return The layered FileObject.
1011      * @throws FileSystemException if an error occurs.
1012      */
1013     @Override
1014     public FileObject createFileSystem(final String scheme,
1015             final FileObject file) throws FileSystemException
1016     {
1017         final FileProvider provider = providers.get(scheme);
1018         if (provider == null)
1019         {
1020             throw new FileSystemException("vfs.impl/unknown-provider.error", scheme, file);
1021         }
1022         return provider.createFileSystem(scheme, file, file.getFileSystem().getFileSystemOptions());
1023     }
1024 
1025     /**
1026      * Creates a layered file system.
1027      * @param file The FileObject to use.
1028      * @return The layered FileObject.
1029      * @throws FileSystemException if an error occurs.
1030      */
1031     @Override
1032     public FileObject createFileSystem(final FileObject file)
1033             throws FileSystemException
1034     {
1035         final String scheme = typeMap.getScheme(file);
1036         if (scheme == null)
1037         {
1038             throw new FileSystemException(
1039                     "vfs.impl/no-provider-for-file.error", file);
1040         }
1041 
1042         return createFileSystem(scheme, file);
1043     }
1044 
1045     /**
1046      * Determines if a layered file system can be created for a given file.
1047      *
1048      * @param file The file to check for.
1049      * @return true if the FileSystem can be created.
1050      * @throws FileSystemException if an error occurs.
1051      */
1052     @Override
1053     public boolean canCreateFileSystem(final FileObject file)
1054             throws FileSystemException
1055     {
1056         return typeMap.getScheme(file) != null;
1057     }
1058 
1059     /**
1060      * Creates a virtual file system.
1061      * @param rootFile The FileObject to use.
1062      * @return The FileObject in the VirtualFileSystem.
1063      * @throws FileSystemException if an error occurs creating the file.
1064      */
1065     @Override
1066     public FileObject createVirtualFileSystem(final FileObject rootFile)
1067             throws FileSystemException
1068     {
1069         return vfsProvider.createFileSystem(rootFile);
1070     }
1071 
1072     /**
1073      * Creates an empty virtual file system.
1074      * @param rootUri The URI to use as the root of the FileSystem.
1075      * @return A FileObject in the virtual FileSystem.
1076      * @throws FileSystemException if an error occurs.
1077      */
1078     @Override
1079     public FileObject createVirtualFileSystem(final String rootUri)
1080             throws FileSystemException
1081     {
1082         return vfsProvider.createFileSystem(rootUri);
1083     }
1084 
1085     /**
1086      * Locates the local file provider.
1087      * <p>
1088      * The local file provider is the first
1089      * {@linkplain #addProvider(String[], FileProvider) provider added}
1090      * implementing {@link LocalFileProvider}.
1091      *
1092      * @return The LocalFileProvider.
1093      * @throws FileSystemException if no local file provider was set.
1094      */
1095     private LocalFileProvider getLocalFileProvider() throws FileSystemException
1096     {
1097         if (localFileProvider == null)
1098         {
1099             throw new FileSystemException(
1100                     "vfs.impl/no-local-file-provider.error");
1101         }
1102         return localFileProvider;
1103     }
1104 
1105     /**
1106      * Get the URLStreamHandlerFactory.
1107      * @return The URLStreamHandlerFactory.
1108      */
1109     @Override
1110     public URLStreamHandlerFactory getURLStreamHandlerFactory()
1111     {
1112         return new VfsStreamHandlerFactory();
1113     }
1114 
1115     /**
1116      * Closes the given filesystem.
1117      * <p>
1118      * If you use VFS as singleton it is VERY dangerous to call this method.
1119      *
1120      * @param filesystem The FileSystem to close.
1121      */
1122     @Override
1123     public void closeFileSystem(final FileSystem filesystem)
1124     {
1125         // inform the cache ...
1126         getFilesCache().clear(filesystem);
1127 
1128         // just in case the cache didnt call _closeFileSystem
1129         _closeFileSystem(filesystem);
1130     }
1131 
1132     /**
1133      * Closes the given file system.
1134      * <p>
1135      * If you use VFS as singleton it is VERY dangerous to call this method
1136      * </p>
1137      *
1138      * @param filesystem The FileSystem to close.
1139      */
1140     public void _closeFileSystem(final FileSystem filesystem)
1141     {
1142         final FileProvider provider = providers.get(filesystem.getRootName().getScheme());
1143         if (provider != null)
1144         {
1145             ((AbstractFileProvider) provider).closeFileSystem(filesystem);
1146         }
1147         else if (filesystem instanceof VirtualFileSystem)
1148         {
1149             // vfsProvider does not implement AbstractFileProvider
1150             vfsProvider.closeFileSystem(filesystem);
1151         }
1152     }
1153 
1154     /**
1155      * This is an internal class because it needs access to the private member
1156      * providers.
1157      */
1158     final class VfsStreamHandlerFactory implements URLStreamHandlerFactory
1159     {
1160         @Override
1161         public URLStreamHandler createURLStreamHandler(final String protocol)
1162         {
1163             final FileProvider provider = providers.get(protocol);
1164             if (provider != null)
1165             {
1166                 return new DefaultURLStreamHandler(context);
1167             }
1168 
1169             // Route all other calls to the default URLStreamHandlerFactory
1170             return new URLStreamHandlerProxy();
1171         }
1172     }
1173 
1174     /**
1175      * Get the schemes currently available.
1176      * @return The array of scheme names.
1177      */
1178     @Override
1179     public String[] getSchemes()
1180     {
1181         final String[] schemes = new String[providers.size()];
1182         providers.keySet().toArray(schemes);
1183         return schemes;
1184     }
1185 
1186     /**
1187      * Get the capabilities for a given scheme.
1188      *
1189      * @param scheme The scheme to located.
1190      * @return A Collection of capabilities.
1191      * @throws FileSystemException if the given scheme is not konwn
1192      */
1193     @Override
1194     public Collection<Capability> getProviderCapabilities(final String scheme)
1195             throws FileSystemException
1196     {
1197         final FileProvider provider = providers.get(scheme);
1198         if (provider == null)
1199         {
1200             throw new FileSystemException("vfs.impl/unknown-scheme.error", scheme);
1201         }
1202 
1203         return provider.getCapabilities();
1204     }
1205 
1206     /**
1207      * Get the configuration builder for the given scheme.
1208      * @param scheme The scheme to locate.
1209      * @return The FileSystemConfigBuilder for the scheme.
1210      * @throws FileSystemException if the given scheme is not konwn
1211      */
1212     @Override
1213     public FileSystemConfigBuilder getFileSystemConfigBuilder(final String scheme)
1214             throws FileSystemException
1215     {
1216         final FileProvider provider = providers.get(scheme);
1217         if (provider == null)
1218         {
1219             throw new FileSystemException("vfs.impl/unknown-scheme.error", scheme);
1220         }
1221 
1222         return provider.getConfigBuilder();
1223     }
1224 
1225     // -- OPERATIONS --
1226 
1227     /**
1228      * Adds the specified FileOperationProvider for the specified scheme.
1229      * Several FileOperationProvider's might be registered for the same scheme.
1230      * For example, for "file" scheme we can register SvnWsOperationProvider and
1231      * CvsOperationProvider.
1232      *
1233      * @param scheme The scheme the provider should be registered for.
1234      * @param operationProvider The FileOperationProvider.
1235      * @throws FileSystemException if an error occurs adding the provider.
1236      */
1237     @Override
1238     public void addOperationProvider(final String scheme,
1239             final FileOperationProvider operationProvider)
1240             throws FileSystemException
1241     {
1242         addOperationProvider(new String[] {scheme}, operationProvider);
1243     }
1244 
1245     /**
1246      * @see FileSystemManager#addOperationProvider(String,
1247      *      org.apache.commons.vfs2.operations.FileOperationProvider)
1248      *
1249      * @param schemes The array of schemes the provider should apply to.
1250      * @param operationProvider The FileOperationProvider.
1251      * @throws FileSystemException if an error occurs.
1252      */
1253     @Override
1254     public void addOperationProvider(final String[] schemes,
1255             final FileOperationProvider operationProvider)
1256             throws FileSystemException
1257     {
1258         for (final String scheme : schemes)
1259         {
1260             if (!operationProviders.containsKey(scheme))
1261             {
1262                 final List<FileOperationProvider> providers = new ArrayList<FileOperationProvider>();
1263                 operationProviders.put(scheme, providers);
1264             }
1265 
1266             final List<FileOperationProvider> providers = operationProviders.get(scheme);
1267 
1268             if (providers.contains(operationProvider))
1269             {
1270                 throw new FileSystemException(
1271                         "vfs.operation/operation-provider-already-added.error", scheme);
1272             }
1273 
1274             setupComponent(operationProvider);
1275 
1276             providers.add(operationProvider);
1277         }
1278     }
1279 
1280     /**
1281      * @param scheme
1282      *            the scheme for wich we want to get the list af registered
1283      *            providers.
1284      *
1285      * @return the registered FileOperationProviders for the specified scheme.
1286      *         If there were no providers registered for the scheme, it returns
1287      *         null.
1288      *
1289      * @throws FileSystemException if an error occurs.
1290      */
1291     @Override
1292     public FileOperationProvider[] getOperationProviders(final String scheme)
1293             throws FileSystemException
1294     {
1295 
1296         final List<?> providers = operationProviders.get(scheme);
1297         if (providers == null || providers.size() == 0)
1298         {
1299             return null;
1300         }
1301         return providers.toArray(new FileOperationProvider[] {});
1302     }
1303 
1304     /**
1305      * Converts a URI into a {@link FileObject}.
1306      *
1307      * @param uri The URI to convert.
1308      * @return The {@link FileObject} that represents the URI.  Never
1309      *         returns null.
1310      * @throws FileSystemException On error converting the URI.
1311      * @since 2.1
1312      */
1313     @Override
1314     public FileObject resolveFile(final URI uri) throws FileSystemException
1315     {
1316         // TODO Push the URI deeper into VFS
1317         return resolveFile(baseFile, uri.toString(), null);
1318     }
1319 
1320     /**
1321      * Converts a URL into a {@link FileObject}.
1322      *
1323      * @param url The URL to convert.
1324      * @return The {@link FileObject} that represents the URL.  Never
1325      *         returns null.
1326      * @throws FileSystemException On error converting the URL.
1327      * @since 2.1
1328      */
1329     @Override
1330     public FileObject resolveFile(final URL url) throws FileSystemException
1331     {
1332         try
1333         {
1334             return this.resolveFile(url.toURI());
1335         }
1336         catch (final URISyntaxException e)
1337         {
1338             throw new FileSystemException(e);
1339         }
1340     }
1341 }