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