/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.lib.loader;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.Iterator;

import org.ow2.jonas.lib.loader.factory.URLFactory;
import org.ow2.jonas.lib.loader.locator.Locator;


/**
 * The <code>AbsModuleClassLoader</code> class is used in JOnAS
 * for loading web, ejbjars and client modules.
 *
 * @author Guillaume Sauthier
 */
public abstract class AbsModuleClassLoader extends URLClassLoader {

    /** factories used to create URLs for URLClassLoader. */
    private URLFactory[] factories;

    /** locators used to search jar/dir for files, directories, ... */
    private Locator[] locators;

    /** bases URLs. */
    private URL[] bases;

    /**
     * Create a new AbsModuleClassLoader for a list of URLs.
     *
     * @param modules the list of URL to be used in the ClassLoader.
     *
     * @throws IOException when Initialization fails.
     */
    public AbsModuleClassLoader(URL[] modules) throws IOException {
        super(new URL[0]);
        bases = modules;
        init();
    }

    /**
     * Create a new AbsModuleClassLoader for a list of URLs with a specified parent loader.
     *
     * @param modules the list of URL to be used in the ClassLoader.
     * @param parent the parent ClassLoader to be used.
     *
     * @throws IOException when Initialization fails.
     */
    public AbsModuleClassLoader(URL[] modules, ClassLoader parent) throws IOException {
        super(new URL[0], parent);
        bases = modules;
        init();
    }

    /**
     * Base Initialization of the ClassLoader, storage of URL list.
     *
     * @throws IOException when URL pointed file is not supported (not an jar nor a directory).
     */
    protected void init() throws IOException {
        factories = new URLFactory[bases.length];
        locators = new Locator[bases.length];

        // Create factories and locator for each URL
        for (int i = 0; i < bases.length; i++) {

            factories[i] = URLFactory.getFactory(bases[i]);
            locators[i] = Locator.getLocator(bases[i]);
        }
    }

    /**
     * Add specified location into the repository.
     * If location is found in multiple URLs of the bases, new
     * URLs will be added multiple times.
     *
     * @param location an entry name (for a jar) or a path name (for a directory)
     *
     * @throws IOException when constructed URL is malformed
     */
    protected void addInRepository(String location) throws IOException {
        // test existence in each base URL
        for (int i = 0; i < bases.length; i++) {
            if (locators[i].hasDirectory(location)
                || locators[i].hasFile(location)) {
                addURL(factories[i].getURL(location));
            }
        }
    }

    /**
     * Add specified location childs into the repository.
     * Search will be performed on the bases URLs.
     *
     * @param location an entry name (for a jar) or a path name (for a directory)
     *
     * @throws IOException When a directory content cannot be explored.
     *
     * TODO test existence is now done in AbsModuleClassLoader
     */
    protected void addContentInRepository(String location) throws IOException {
        // test existence in each base URL
        for (int i = 0; i < bases.length; i++) {
            List list = locators[i].listContent(location);
            for (Iterator l = list.iterator(); l.hasNext();) {
                // TODO Exclude non jar content !
                addURL(factories[i].getURL((String) l.next()));
            }
        }
    }

    /**
     * @return Returns a String representation of the AbsModuleClassLoader
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();

        sb.append("classloader : " + getClass().getName() + "\n");
        sb.append("\tmodules bases (not in loader!) : \n");
        for (int i = 0; i < bases.length; i++) {
            sb.append("\t\t -" + bases[i] + "\n");
        }
        sb.append("\trepositories :\n");
        URL[] rep = getURLs();
        for (int i = 0; i < rep.length; i++) {
            sb.append("\t\t -" + rep[i] + "\n");
        }
        sb.append("\tparent : " + getParent() + "\n");

        return sb.toString();
    }

    /**
     * @return Returns the bases.
     */
    public URL[] getBases() {
        return bases;
    }

    /**
     * @return Returns a String representation of the classpath used by this classloader
     */
    public String getClasspath() {
        URL[] urls = getURLs();
        StringBuffer cp = new StringBuffer();
        for (int i = 0; i < urls.length; i++) {
            String url = urls[i].getFile();
            // do not add URL with !/ inside
            if (url.indexOf("!/") == -1) {
                cp.append(File.pathSeparator + url);
            }
        }
        if (getParent() instanceof AbsModuleClassLoader) {
            AbsModuleClassLoader module = (AbsModuleClassLoader) getParent();
            cp.append(module.getClasspath());
        }
        return  cp.toString();
    }

    /**
     * Appends the specified URL to the list of URLs to search for
     * classes and resources.
     * @param url the URL to be added to the search path of URLs
     */
    public void addURL(URL url) {
        super.addURL(url);
    }
}
