/*
 
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
*
 
* This code is free software; you can redistribute it and/or modify it
 
* under the terms of the GNU General Public License version 2 only, as
 
* published by the Free Software Foundation.
  
Oracle designates this
 
* particular file as subject to the "Classpath" exception as provided
 
* by Oracle in the LICENSE file that accompanied this code.
 
*
 
* This code is distributed in the hope that it will be useful, but WITHOUT
 
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
* FITNESS FOR A PARTICULAR PURPOSE.
  
See the GNU General Public License
 
* version 2 for more details (a copy is included in the LICENSE file that
 
* accompanied this code).
 
*
 
* You should have received a copy of the GNU General Public License version
 
* 2 along with this work; if not, write to the Free Software Foundation,
 
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
*
 
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 
* or visit www.oracle.com if you need additional information or have any
 
* questions.
 
*/

package java.nio.file;

import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.lang.reflect.Constructor;

/**
 
* Factory methods for file systems. This class defines the {@link #getDefault
 
* getDefault} method to get the default file system and factory methods to
 
* construct other types of file systems.
 
*
 
* <p> The first invocation of any of the methods defined by this class causes
 
* the default {@link FileSystemProvider provider} to be loaded. The default
 
* provider, identified by the URI scheme "file", creates the {@link FileSystem}
 
* that provides access to the file systems accessible to the Java virtual
 
* machine. If the process of loading or initializing the default provider fails
 
* then an unspecified error is thrown.
 
*
 
* <p> The first invocation of the {@link FileSystemProvider#installedProviders
 
* installedProviders} method, by way of invoking any of the {@code
 
* newFileSystem} methods defined by this class, locates and loads all
 
* installed file system providers. Installed providers are loaded using the
 
* service-provider loading facility defined by the {@link ServiceLoader} class.
 
* Installed providers are loaded using the system class loader. If the
 
* system class loader cannot be found then the extension class loader is used;
 
* if there is no extension class loader then the bootstrap class loader is used.
 
* Providers are typically installed by placing them in a JAR file on the
 
* application class path or in the extension directory, the JAR file contains a
 
* provider-configuration file named {@code java.nio.file.spi.FileSystemProvider}
 
* in the resource directory {@code META-INF/services}, and the file lists one or
 
* more fully-qualified names of concrete subclass of {@link FileSystemProvider}
 
* that have a zero argument constructor.
 
* The ordering that installed providers are located is implementation specific.
 
* If a provider is instantiated and its {@link FileSystemProvider#getScheme()
 
* getScheme} returns the same URI scheme of a provider that was previously
 
* instantiated then the most recently instantiated duplicate is discarded. URI
 
* schemes are compared without regard to case. During construction a provider
 
* may safely access files associated with the default provider but care needs
 
* to be taken to avoid circular loading of other installed providers. If
 
* circular loading of installed providers is detected then an unspecified error
 
* is thrown.
 
*
 
* <p> This class also defines factory methods that allow a {@link ClassLoader}
 
* to be specified when locating a provider. As with installed providers, the
 
* provider classes are identified by placing the provider configuration file
 
* in the resource directory {@code META-INF/services}.
 
*
 
* <p> If a thread initiates the loading of the installed file system providers
 
* and another thread invokes a method that also attempts to load the providers
 
* then the method will block until the loading completes.
 
*
 
* @since 1.7
 
*/


public final class FileSystems {
    
private FileSystems() {
    
}

    
// lazy initialization of default file system
    
private static class DefaultFileSystemHolder {
        
static final FileSystem defaultFileSystem = defaultFileSystem();

        
// returns default file system
        
private static FileSystem defaultFileSystem() {
            
// load default provider
            
FileSystemProvider provider = AccessController
                
.doPrivileged
(new PrivilegedAction<FileSystemProvider>() {
                    
public FileSystemProvider run() {
                        
return getDefaultProvider();
                    
}
                
});

            
// return file system
            
return provider.getFileSystem(URI.create("file:///"));
        
}

        
// returns default provider
        
private static FileSystemProvider getDefaultProvider() {
            
FileSystemProvider provider = sun.nio.fs.DefaultFileSystemProvider.create();

            
// if the property java.nio.file.spi.DefaultFileSystemProvider is
            
// set then its value is the name of the default provider (or a list)
            
String propValue = System
                
.getProperty
("java.nio.file.spi.DefaultFileSystemProvider");
            
if (propValue != null) {
                
for (String cn: propValue.split(",")) {
                    
try {
                        
Class<?> c = Class
                            
.forName
(cn, true, ClassLoader.getSystemClassLoader());
                        
Constructor<?> ctor = c
                            
.getDeclaredConstructor
(FileSystemProvider.class);
                        
provider = (FileSystemProvider)ctor.newInstance(provider);

                        
// must be "file"
                        
if (!provider.getScheme().equals("file"))
                            
throw new Error("Default provider must use scheme 'file'");

                    
} catch (Exception x) {
                        
throw new Error(x);
                    
}
                
}
            
}
            
return provider;
        
}
    
}

    
/**
     
* Returns the default {@code FileSystem}. The default file system creates
     
* objects that provide access to the file systems accessible to the Java
     
* virtual machine. The <em>working directory</em> of the file system is
     
* the current user directory, named by the system property {@code user.dir}.
     
* This allows for interoperability with the {@link java.io.File java.io.File}
     
* class.
     
*
     
* <p> The first invocation of any of the methods defined by this class
     
* locates the default {@link FileSystemProvider provider} object. Where the
     
* system property {@code java.nio.file.spi.DefaultFileSystemProvider} is
     
* not defined then the default provider is a system-default provider that
     
* is invoked to create the default file system.
     
*
     
* <p> If the system property {@code java.nio.file.spi.DefaultFileSystemProvider}
     
* is defined then it is taken to be a list of one or more fully-qualified
     
* names of concrete provider classes identified by the URI scheme
     
* {@code "file"}. Where the property is a list of more than one name then
     
* the names are separated by a comma. Each class is loaded, using the system
     
* class loader, and instantiated by invoking a one argument constructor
     
* whose formal parameter type is {@code FileSystemProvider}. The providers
     
* are loaded and instantiated in the order they are listed in the property.
     
* If this process fails or a provider's scheme is not equal to {@code "file"}
     
* then an unspecified error is thrown. URI schemes are normally compared
     
* without regard to case but for the default provider, the scheme is
     
* required to be {@code "file"}. The first provider class is instantiated
     
* by invoking it with a reference to the system-default provider.
     
* The second provider class is instantiated by invoking it with a reference
     
* to the first provider instance. The third provider class is instantiated
     
* by invoking it with a reference to the second instance, and so on. The
     
* last provider to be instantiated becomes the default provider; its {@code
     
* getFileSystem} method is invoked with the URI {@code "file:///"} to
     
* get a reference to the default file system.
     
*
     
* <p> Subsequent invocations of this method return the file system that was
     
* returned by the first invocation.
     
*
     
* @return
  
the default file system
     
*/

    
public static FileSystem getDefault() {
        
return DefaultFileSystemHolder.defaultFileSystem;
    
}

    
/**
     
* Returns a reference to an existing {@code FileSystem}.
     
*
     
* <p> This method iterates over the {@link FileSystemProvider#installedProviders()
     
* installed} providers to locate the provider that is identified by the URI
     
* {@link URI#getScheme scheme} of the given URI. URI schemes are compared
     
* without regard to case. The exact form of the URI is highly provider
     
* dependent. If found, the provider's {@link FileSystemProvider#getFileSystem
     
* getFileSystem} method is invoked to obtain a reference to the {@code
     
* FileSystem}.
     
*
     
* <p> Once a file system created by this provider is {@link FileSystem#close
     
* closed} it is provider-dependent if this method returns a reference to
     
* the closed file system or throws {@link FileSystemNotFoundException}.
     
* If the provider allows a new file system to be created with the same URI
     
* as a file system it previously created then this method throws the
     
* exception if invoked after the file system is closed (and before a new
     
* instance is created by the {@link #newFileSystem newFileSystem} method).
     
*
     
* <p> If a security manager is installed then a provider implementation
     
* may require to check a permission before returning a reference to an
     
* existing file system. In the case of the {@link FileSystems#getDefault
     
* default} file system, no permission check is required.
     
*
     
* @param
   
uri
  
the URI to locate the file system
     
*
     
* @return
  
the reference to the file system
     
*
     
* @throws
  
IllegalArgumentException
     
*
          
if the pre-conditions for the {@code uri} parameter are not met
     
* @throws
  
FileSystemNotFoundException
     
*
          
if the file system, identified by the URI, does not exist
     
* @throws
  
ProviderNotFoundException
     
*
          
if a provider supporting the URI scheme is not installed
     
* @throws
  
SecurityException
     
*
          
if a security manager is installed and it denies an unspecified
     
*
          
permission
     
*/

    
public static FileSystem getFileSystem(URI uri) {
        
String scheme = uri.getScheme();
        
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            
if (scheme.equalsIgnoreCase(provider.getScheme())) {
                
return provider.getFileSystem(uri);
            
}
        
}
        
throw new ProviderNotFoundException("Provider \"" + scheme + "\" not found");
    
}

    
/**
     
* Constructs a new file system that is identified by a {@link URI}
     
*
     
* <p> This method iterates over the {@link FileSystemProvider#installedProviders()
     
* installed} providers to locate the provider that is identified by the URI
     
* {@link URI#getScheme scheme} of the given URI. URI schemes are compared
     
* without regard to case. The exact form of the URI is highly provider
     
* dependent. If found, the provider's {@link FileSystemProvider#newFileSystem(URI,Map)
     
* newFileSystem(URI,Map)} method is invoked to construct the new file system.
     
*
     
* <p> Once a file system is {@link FileSystem#close closed} it is
     
* provider-dependent if the provider allows a new file system to be created
     
* with the same URI as a file system it previously created.
     
*
     
* <p> <b>Usage Example:</b>
     
* Suppose there is a provider identified by the scheme {@code "memory"}
     
* installed:
     
* <pre>
     
*
   
Map&lt;String,String&gt; env = new HashMap&lt;&gt;();
     
*
   
env.put("capacity", "16G");
     
*
   
env.put("blockSize", "4k");
     
*
   
FileSystem fs = FileSystems.newFileSystem(URI.create("memory:///?name=logfs"), env);
     
* </pre>
     
*
     
* @param
   
uri
     
*
          
the URI identifying the file system
     
* @param
   
env
     
*
          
a map of provider specific properties to configure the file system;
     
*
          
may be empty
     
*
     
* @return
  
a new file system
     
*
     
* @throws
  
IllegalArgumentException
     
*
          
if the pre-conditions for the {@code uri} parameter are not met,
     
*
          
or the {@code env} parameter does not contain properties required
     
*
          
by the provider, or a property value is invalid
     
* @throws
  
FileSystemAlreadyExistsException
     
*
          
if the file system has already been created
     
* @throws
  
ProviderNotFoundException
     
*
          
if a provider supporting the URI scheme is not installed
     
* @throws
  
IOException
     
*
          
if an I/O error occurs creating the file system
     
* @throws
  
SecurityException
     
*
          
if a security manager is installed and it denies an unspecified
     
*
          
permission required by the file system provider implementation
     
*/

    
public static FileSystem newFileSystem(URI uri, Map<String,?> env)
        
throws IOException
    
{
        
return newFileSystem(uri, env, null);
    
}

    
/**
     
* Constructs a new file system that is identified by a {@link URI}
     
*
     
* <p> This method first attempts to locate an installed provider in exactly
     
* the same manner as the {@link #newFileSystem(URI,Map) newFileSystem(URI,Map)}
     
* method. If none of the installed providers support the URI scheme then an
     
* attempt is made to locate the provider using the given class loader. If a
     
* provider supporting the URI scheme is located then its {@link
     
* FileSystemProvider#newFileSystem(URI,Map) newFileSystem(URI,Map)} is
     
* invoked to construct the new file system.
     
*
     
* @param
   
uri
     
*
          
the URI identifying the file system
     
* @param
   
env
     
*
          
a map of provider specific properties to configure the file system;
     
*
          
may be empty
     
* @param
   
loader
     
*
          
the class loader to locate the provider or {@code null} to only
     
*
          
attempt to locate an installed provider
     
*
     
* @return
  
a new file system
     
*
     
* @throws
  
IllegalArgumentException
     
*
          
if the pre-conditions for the {@code uri} parameter are not met,
     
*
          
or the {@code env} parameter does not contain properties required
     
*
          
by the provider, or a property value is invalid
     
* @throws
  
FileSystemAlreadyExistsException
     
*
          
if the URI scheme identifies an installed provider and the file
     
*
          
system has already been created
     
* @throws
  
ProviderNotFoundException
     
*
          
if a provider supporting the URI scheme is not found
     
* @throws
  
ServiceConfigurationError
     
*
          
when an error occurs while loading a service provider
     
* @throws
  
IOException
     
*
          
an I/O error occurs creating the file system
     
* @throws
  
SecurityException
     
*
          
if a security manager is installed and it denies an unspecified
     
*
          
permission required by the file system provider implementation
     
*/

    
public static FileSystem newFileSystem(URI uri, Map<String,?> env, ClassLoader loader)
        
throws IOException
    
{
        
String scheme = uri.getScheme();

        
// check installed providers
        
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            
if (scheme.equalsIgnoreCase(provider.getScheme())) {
                
return provider.newFileSystem(uri, env);
            
}
        
}

        
// if not found, use service-provider loading facility
        
if (loader != null) {
            
ServiceLoader<FileSystemProvider> sl = ServiceLoader
                
.load
(FileSystemProvider.class, loader);
            
for (FileSystemProvider provider: sl) {
                
if (scheme.equalsIgnoreCase(provider.getScheme())) {
                    
return provider.newFileSystem(uri, env);
                
}
            
}
        
}

        
throw new ProviderNotFoundException("Provider \"" + scheme + "\" not found");
    
}

    
/**
     
* Constructs a new {@code FileSystem} to access the contents of a file as a
     
* file system.
     
*
     
* <p> This method makes use of specialized providers that create pseudo file
     
* systems where the contents of one or more files is treated as a file
     
* system.
     
*
     
* <p> This method iterates over the {@link FileSystemProvider#installedProviders()
     
* installed} providers. It invokes, in turn, each provider's {@link
     
* FileSystemProvider#newFileSystem(Path,Map) newFileSystem(Path,Map)} method
     
* with an empty map. If a provider returns a file system then the iteration
     
* terminates and the file system is returned. If none of the installed
     
* providers return a {@code FileSystem} then an attempt is made to locate
     
* the provider using the given class loader. If a provider returns a file
     
* system then the lookup terminates and the file system is returned.
     
*
     
* @param
   
path
     
*
          
the path to the file
     
* @param
   
loader
     
*
          
the class loader to locate the provider or {@code null} to only
     
*
          
attempt to locate an installed provider
     
*
     
* @return
  
a new file system
     
*
     
* @throws
  
ProviderNotFoundException
     
*
          
if a provider supporting this file type cannot be located
     
* @throws
  
ServiceConfigurationError
     
*
          
when an error occurs while loading a service provider
     
* @throws
  
IOException
     
*
          
if an I/O error occurs
     
* @throws
  
SecurityException
     
*
          
if a security manager is installed and it denies an unspecified
     
*
          
permission
     
*/

    
public static FileSystem newFileSystem(Path path,
                                           
ClassLoader loader)
        
throws IOException
    
{
        
if (path == null)
            
throw new NullPointerException();
        
Map<String,?> env = Collections.emptyMap();

        
// check installed providers
        
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            
try {
                
return provider.newFileSystem(path, env);
            
} catch (UnsupportedOperationException uoe) {
            
}
        
}

        
// if not found, use service-provider loading facility
        
if (loader != null) {
            
ServiceLoader<FileSystemProvider> sl = ServiceLoader
                
.load
(FileSystemProvider.class, loader);
            
for (FileSystemProvider provider: sl) {
                
try {
                    
return provider.newFileSystem(path, env);
                
} catch (UnsupportedOperationException uoe) {
                
}
            
}
        
}

        
throw new ProviderNotFoundException("Provider not found");
    
}
}