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