/*
 * (c) Copyright 2003, 2004 Hewlett-Packard Development Company, LP
 * [See end of file]
 */

package org.joseki.server;

import java.io.* ;
import java.util.* ;
import org.apache.commons.logging.*; 
import javax.servlet.* ;

import java.net.URL; 
import java.net.MalformedURLException; 

import com.hp.hpl.jena.rdf.model.* ;
import com.hp.hpl.jena.util.ModelLoader ;


/** <p>A class to encapsulate accessing a file, whether it is
 *  in the local file system (various places), in a JAR file or a URL.
 *  Places to look for a file with a relative path name are: the servlet context,
 *  webserver current directory.</p>
 * 
 *  <p>Error handling: on errors, a log message is written and nulls are returned</p> 
 * 
 * @author     Andy Seaborne
 * @version    $Id: FileManager.java,v 1.10 2004/04/30 14:13:13 andy_seaborne Exp $
 */
 
public class FileManager
{
    static Log logger = LogFactory.getLog(FileManager.class) ;

    static FileManager instance = null ;

    List handler = new ArrayList() ;
    ServletContext servletContext = null ;

    private FileManager()
    {
        handler.add(new FileLocator()) ;
        handler.add(new ServletResourceLocator()) ;

        handler.add(new ClassLoaderLocator(Thread.currentThread().getContextClassLoader())) ;    
        //handler.add(new ClassLoaderLocator(this.getClass().getClassLoader())) ;    
        handler.add(new ClassLoaderLocator(ClassLoader.getSystemClassLoader())) ;    
    }

    /** Get the file manager.
     * @return the file manager
     */
    public static FileManager getInstance()
    {
        // Sungleton pattern adopted in case we later have several file managers.
        if ( instance == null )
            instance = new FileManager() ;
        return instance ;
    }
    
    public void setServletContext(ServletContext _servletContext)
    {
        logger.trace("Setting servlet context") ;
        servletContext = _servletContext ;
    }
    
    /** Load a model from a file (local or remote).
     *  Guesses the syntax of the file based on filename extension, 
     *  defaulting to RDF/XML.
     *  @param filenameOrURI The filename or a URI (file:, http:)
     *  @return a new model, or null on error.
     */

    public Model loadModel(String filenameOrURI)
    { return loadModel(filenameOrURI, null) ; }

    /** Load a model from a file (local or remote).
     *  Guesses the syntax of the file based on filename extension, 
     *  defaulting to RDF/XML.
     * 
     *  @param filenameOrURI The filename or a URI (file:, http:)
     *  @param baseURI  Base URI for loading the RDF model.
     *  @return a new model, or null on error.
     */

    public Model loadModel(String filenameOrURI, String baseURI)
    {
        Model m = ModelFactory.createDefaultModel() ;
        return readModel(m, filenameOrURI, null) ;
    }
    
    /**
     * Read a file of RDF into a model.
     * @param model
     * @param filenameOrURI
     * @return The model or null, if there was an error.
     */    

    public Model readModel(Model model, String filenameOrURI)
    { return readModel(model, filenameOrURI, null); }
    
    /**
     * Read a file of RDF into a model.
     * @param model
     * @param filenameOrURI
     * @param baseURI
     * @return The model or null, if there was an error.
     */    

    public Model readModel(Model model, String filenameOrURI, String baseURI)
    
    {
        if ( baseURI == null )
            baseURI = "" ;
        
        //String syntax = ModelLoader.guessLang(filenameOrURI, "RDF/XML") ;
        String syntax = ModelLoader.guessLang(filenameOrURI) ;
        if ( syntax == null || syntax.equals("") )
            syntax = "RDF/XML" ;

        InputStream in = open(filenameOrURI) ;
        if ( in == null )
        {
            logger.debug("Failed to locate '"+filenameOrURI+"'") ;
            return null ;
        }
        model.read(in,baseURI, syntax) ;
        return model ;
    }


    public InputStream open(String filenameOrURI)
    {
        for ( Iterator iter = handler.iterator() ; iter.hasNext() ; )
        {
            Locator loc = (Locator)iter.next() ;
            InputStream in = loc.open(filenameOrURI) ;
            if ( in != null )
                return in ;
        }
        return null; 
    }
 

    private interface Locator
    {
        public InputStream open(String filenameOrURI) ;
    }
   
    private class FileLocator implements Locator
    {
        FileLocator()
        {
            try {
                //String wd = System.getProperty("user.dir") ;
                String wd = new File(".").getCanonicalPath() ;
                logger.info("Base directory: "+wd) ;
            } catch (IOException ex)
            {
                logger.error("Failed to discover the working directory", ex) ;
            }
        }
        
        public InputStream open(String filenameOrURI)
        {
            String fn = toFilename(filenameOrURI) ;
            if ( fn == null )
                return null ;
                         
            File f = new File(fn) ;
        
            if ( !f.exists() )
            {
                logger.trace("FileLocator: File not found: "+filenameOrURI) ; 
                return null ;
            }
            
            try {
                InputStream in = new FileInputStream(fn) ;
                if ( in == null )
                {
                    // Should not happen 
                    logger.trace("FileLocator: Failed to open: "+filenameOrURI) ;
                    return null ;
                }
                
                logger.debug("Read as file: "+filenameOrURI) ;
                // Create base -- Java 1.4-isms
                //base = f.toURI().toURL().toExternalForm() ;
                //base = base.replaceFirst("^file:/([^/])", "file:///$1") ;
                return in ;
            } catch (IOException ioEx)
            {
                // Includes FileNotFoundException
                // We already tested whether the file exists or not.
                logger.warn("File unreadable (but exists): "+fn+" Exception: "+ioEx.getMessage()) ;
                return null ;
            }
        }
    }
    
    private class URLLocator implements Locator
    {
        public InputStream open(String filenameOrURI)
        {
            if (isFile(filenameOrURI))
                return null;
            try
            {
                URL url = new URL(filenameOrURI);
                InputStream in = new BufferedInputStream(url.openStream());
                if ( in == null )
                {
                    logger.trace("URLLocator: not found: "+filenameOrURI) ; 
                    return null ;
                }
                
                logger.debug("Read as URL: " + filenameOrURI);
                return in;
            }
            catch (MalformedURLException ex)
            {
                logger.warn("Malformed URL: " + filenameOrURI);
                return null;
            }
            catch (IOException ex)
            {
                logger.warn("IO Exception opening URL: " + filenameOrURI);
                return null;
            }
        }
    }
    
    
    private class ServletResourceLocator implements Locator
    {
        public InputStream open(String filenameOrURI)
        {
            if ( servletContext == null)
                return null ;

            String fn = toFilename(filenameOrURI) ;
            if ( fn == null )
            {
                logger.trace("ServletResourceLocator: failed to open: "+filenameOrURI) ; 
                return null ;
            }

            InputStream in = servletContext.getResourceAsStream(fn);
            //base = servletContext.getResource(fn).toExternalForm();
            if (in != null)
                logger.debug("Reading as servlet resource: " + filenameOrURI);
            return in ;
        }
    }
    
    private class ClassLoaderLocator  implements Locator
    {
        ClassLoader classLoader = null ;
        ClassLoaderLocator(ClassLoader _classLoader)
        {
            classLoader =_classLoader ;
        }
        
        
        public InputStream open(String filenameOrURI)
        {
            if ( classLoader == null )
                return null ;
                
            String fn = toFilename(filenameOrURI) ;
            if ( fn == null )
                return null ;

            InputStream in = classLoader.getResourceAsStream(fn) ;
            if ( in == null )
            {
                logger.trace("ClassLoaderLocator: failed to open: "+filenameOrURI) ;
            }
            
            // base = classLoader.getResource(fn).toExternalForm ;       
            logger.debug("Reading as resource: "+filenameOrURI) ;
            return in ;
        }
    }
    
    private String toFilename(String filenameOrURI)
    {
        if ( !isFile(filenameOrURI) )
            return null ;

        String fn = filenameOrURI ;
        if ( fn.startsWith("file:") )
            fn = fn.substring("file:".length()) ;
        return fn ;
    }
    
    private boolean isFile(String name)
    {
        return name.startsWith("file:") || ! isURI(name) ;
    }
    
    
    private boolean isURI(String name)
    {
        if ( name.matches("[^/:]*:.*") )
            return true ;
        return false ;
    }
}



/*
 *  (c) Copyright 2003, 2004 Hewlett-Packard Development Company, LP
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
