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