// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;

public final class URLUtil
{

    /* keep people from creating this class */
    private URLUtil ()
    {
    }
    
    /**
        Generate a File for a URL.

        @param urlContext a URL to open in a file. Assumes URL is of
        type file protocol (e.g. file://) and is not null.

        @return a File representing the URL, or <b>null</b> if the file
        could not be opened
    */
    public static File file (URL urlContext, boolean warning)
    {
        String protocol = urlContext.getProtocol();
        if (!protocol.equals("file")) {
            if (warning) {
                Log.util.log(
                    Level.WARNING,
                    "Invalid protocol for use with URLUtil.file(url): {0}",
                    urlContext);
            }
            return null;
        }
        String filePath = urlContext.getFile();
        if (filePath.endsWith("/.")) {
            filePath = filePath.substring(0, filePath.length() - 1);
        }
            // interestingly if one constructs a file with invalid
            // separatorChar (e.g. '/' on windows) most File members
            // work (e.g. exists()) but getParent() does not. Thus we
            // have to flip the separator on Windows.
        return new File(filePath.replace('/', File.separatorChar));
    }

    /**
        Returns true if the URL may exist.

        If the URL is a file: url, we convert it to a File and call
        exists(), which is faster than dealing with a
        FileNotFoundException.

        This is primarily when the file is likely not to exist, such
        as in the ResourceService which probes many possible filenames.
    */
    public static boolean maybeURLExists (URL url)
    {
        File file = URLUtil.file(url, false);
        if (file == null) {
                // it may exist, we aren't sure.
            return true;
        }
        return file.exists();
    }

    /**

        @param context the context in which to parse the
        specification.
        @param spec a string representation of a URL.

        @return a new URL, or <b>null</b> if there was an exception in
        the creation

        @see java.net.URL#URL(URL, String)
    */
    public static URL asURL (URL context, String spec)
    {
            // Needed for java 1.2 bug - PR 12590
        if ("".equals(spec) &&
            context != null &&
            "file".equals(context.getProtocol()))
        {
            spec = "./.";
        }
        try {
            return new URL(context, spec);
        }
        catch (MalformedURLException e) {
            Log.util.log(Level.WARNING,
                         "Could not create URL for {0}: {1}",
                         new Object[]{ spec, e}
                         );
            return null;
        }
    }

    /**
        Creates a URL relative to the application working directory.  This
        method should only be used within server code or by command line (i.e.
        non-GUI) clients.

        If you are in GUI code, use Widgets.url().
        If you are in shared code, use Base.service().url().

    */
    public static URL url (File file)
    {
        return url(file.getPath());
    }

    /**
        Creates a URL relative to the application working directory.  This
        method should only be used within server code or by command line (i.e.
        non-GUI) clients.

        If you are in GUI code, use Widgets.url().
        If you are in shared code, use Base.service().url().

    */
    public static URL url (String spec)
    {
        return asURL(url(), spec);
    }

    /**
        Creates a URL for the application working directory.  This method
        should only be used within server code or by command line clients.

        If you are in GUI code, use Application.codeBase().

    */
    public static URL url ()
    {
        return urlAbsolute(SystemUtil.getCwdFile());
    }


    /**
        Creates a URL that references the given file.  This method
        should only be used within server code or by command line clients.

    */
    public static URL urlAbsolute (File file)
    {
        String filePath = file.getAbsolutePath();
        filePath = filePath.replace(File.separatorChar, '/');

        if (file.exists() && file.isDirectory()) {
            if (filePath.endsWith("/.")) {
                filePath = filePath.substring(0, filePath.length() - 1);
            }
            if (!filePath.endsWith("/")) {
                filePath = Fmt.S("%s/", filePath);
            }
        }
        try {
            /*
                If the file path starts with "/" then don't prepend
                one. Adding the extra slash will yield 'file://..."
                which causes the first node of the path to be
                considered a hostname not part of the path.
            */
            if (filePath.startsWith("/")) {
                return new URL("file", null, Fmt.S("%s", filePath));
            }
            else {
                return new URL("file", null, Fmt.S("/%s", filePath));
            }
        }
        catch (MalformedURLException e) {
            Log.util.log(Level.WARNING,
                         "Cannot establish base URL {0}: {1}",
                         new Object[]{filePath, e}
                         );
            return null;
        }
    }

    /**
        Returns whether the specified specification is a
        fully-qualified URL.  The heuristic that we use is to check
        whether it contains the string ":/" before any other
        slashes.  Note:  IFC file codebases a prefixed by "file:/".
        This is why we check for only one slash.

    */
    public static boolean fullyQualifiedURLSpec (String spec)
    {
        int slash = spec.indexOf("/");
        int colon = spec.indexOf(":");

            // We have a complete URL if we have a protocol (the first slash
            // is preceded by a colon)
        return (slash > 0 && colon == slash - 1);
    }
    
    /**
        Create a URL to a web server. If the url is absolute (includes
        http) use it as is.
        
        Otherwise, assume the URL is relative to the <code>context</code>.

        @param url the URL string we are making a URL object for
        @param context the root URL

        @return a new URL as requested
        @exception MalformedURLException
    */
    public static URL formURL (String url, String context)
      throws MalformedURLException
    {
        URL completeURL = null;
        if (url.indexOf(HTTP.Protocol) == 0) {
            completeURL = new URL(url);
        }
        else {
            completeURL = new URL(new URL(Fmt.S("%s/", context)), url);
        }

        return completeURL;
    }

    /**
        Create a URL from a path and file name.  The resulting URL will
        be constructed independently of what VM (1.1 or 1.2) is being
        used.

        @param path the path (either absolute or relative)
        @param file the file name.

        @return a new URL as requested
    */
    public static URL concatURL (String path, String file)
    {
        String string = Fmt.S("%s/%s", path, file);
        try {
            return new URL(string);
        }
        catch (MalformedURLException e) {
            Log.util.log(Level.WARNING,
                         "Could not create URL for {0}: {1}",
                         new Object[]{string, e}
                         );
            return null;
        }
    }
    
}
