/*
 * Copyright to the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.rioproject.loader;

import edu.emory.mathcs.util.classloader.URIClassLoader;
import net.jini.loader.ClassAnnotation;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Properties;

/**
 * The ServiceClassLoader overrides getURLs(), ensuring all classes that need to
 * be annotated with specific location(s) are returned appropriately
 *
 * @author Dennis Reedy
 */
public class ServiceClassLoader extends URIClassLoader implements ClassAnnotation {
    private URI[] searchPath;
    /** The ClassAnnotator to use */
    private ClassAnnotator annotator;
    /** Meta data associated with the classloader */
    private Properties metaData = new Properties();

    /**
     * Constructs a new ServiceClassLoader for the specified URLs having the
     * given parent. The constructor takes two sets of URLs. The first set is
     * where the class loader loads classes from, the second set is what it
     * returns when getURLs() is called.
     * 
     * @param searchPath Array of URIs to search for classes
     * @param annotator Array of URLs to use for the codebase
     * @param parent Parent ClassLoader to delegate to
     */
    public ServiceClassLoader(URI[] searchPath,
                              ClassAnnotator annotator,
                              ClassLoader parent) {
        this(searchPath, annotator, parent, null);
    }

    /**
     * Constructs a new ServiceClassLoader for the specified URLs having the
     * given parent. The constructor takes two sets of URLs. The first set is
     * where the class loader loads classes from, the second set is what it
     * returns when getURLs() is called.
     *
     * @param searchPath Array of URIs to search for classes
     * @param annotator Array of URLs to use for the codebase
     * @param parent Parent ClassLoader to delegate to
     * @param metaData Optional meta data associated with the classloader
     */
    public ServiceClassLoader(URI[] searchPath,
                              ClassAnnotator annotator,
                              ClassLoader parent,
                              Properties metaData) {
        super(searchPath, parent);
        if(annotator==null)
            throw new NullPointerException("annotator is null");
        this.annotator = annotator;
        this.searchPath = searchPath;
        if(metaData!=null)
            this.metaData.putAll(metaData);
    }

    /**
     * Get the {@link org.rioproject.boot.ClassAnnotator} created at construction
     * time
     *
     * @return The ClassAnnotator
     */
    public ClassAnnotator getClassAnnotator() {
        return(annotator);
    }
    
    /**
     * Get the meta data associated with this classloader
     *
     * @return A Properties object representing any meta data associated with
     * this classloader. A new Properties object is created each time
     */
    public Properties getMetaData() {
        return(new Properties(metaData));
    }

    /**
     * Add meta data associated with the classloader
     *
     * @param metaData Properties to associate to this classloader. If the
     * property already exists in the managed metaData, it will be replaced.
     * New properties will be added. A null parameter will be ignored.
     */
    public void addMetaData(Properties metaData) {
        if(metaData==null)
            return;
        this.metaData.putAll(metaData);
    }

    /**
     * Get the URLs to be used for class annotations as determined by the
     * {@link org.rioproject.boot.ClassAnnotator}
     */
    public URL[] getURLs() {
        return(annotator.getURLs());
    }
    
    /**
     * Get the search path of URLs for loading classes and resources
     *
     * @return The array of <code>URL[]</code> which corresponds to the search
     * path for the class loader; that is, the array elements are the locations
     * from which the class loader will load requested classes.
     *
     * @throws MalformedURLException If any of the URis cannot be transformed
     * to URLs
     */
    public URL[] getSearchPath() throws MalformedURLException {
        URL[] urls;
        if(searchPath != null) {
            urls = new URL[searchPath.length];
            for(int i=0; i<urls.length; i++)
                urls[i] = searchPath[i].toURL();
        } else {
            urls = new URL[0];
        }
        return (urls);
    }

    /**
     * Appends the specified URLs to the list of URLs to search for classes and
     * resources.
     *
     * @param urls The URLs to add
     */
    public void addURLs(URL[] urls) {
        URI[] uris = new URI[0];
        try {
            uris = getURIs(urls);
        } catch (URISyntaxException e) {
            e.printStackTrace();  
        }
        for (URI uri : uris)
            super.addURI(uri);
    }

    /**
     * Get the class annotations as determined by the
     * {@link org.rioproject.boot.ClassAnnotator}
     *
     * @see net.jini.loader.ClassAnnotation#getClassAnnotation
     */
    public String getClassAnnotation() {
        return (annotator.getClassAnnotation());
    }
    
    /**
     * Returns a String representation of this class loader.
     **/
    public String toString() {
        return(ServiceClassLoader.class.getName()+" "+
               "ClassPath : ["+ClassAnnotator.urisToPath(searchPath)+"] "+
               "Codebase : ["+getClassAnnotation()+"]");
    }

    /**
     * Convert a <code>URL[]</code> into a <code>URI[]</code>
     *
     * @param urls Array of URLs to convert
     *
     * @return Converted array of URIs
     *
     * @throws URISyntaxException If there are errors converting the URLs to
     * URIs
     */
    public static URI[] getURIs(URL[] urls) throws URISyntaxException {
        if(urls==null)
            throw new IllegalArgumentException("urls array must not be null");
        URI[] uris = new URI[urls.length];
        for(int i=0; i<urls.length; i++) {
            if(urls[i].getProtocol().equals("file")) {
                File f = new File(urls[i].getFile());
                if(f.getAbsolutePath().contains("%20") ) {
                    String path = f.getAbsolutePath().replaceAll("%20", " ");
                    f = new File(path);
                }
                uris[i] = f.toURI();
            } else {
                uris[i] = urls[i].toURI();
            }
        }
        return(uris);
    }

}

@@ -3144,36 +3144,38 @@
  org.rioproject.
-boot
+loader
 .ClassAnnotator%7D
@@ -4209,36 +4209,38 @@
  org.rioproject.
-boot
+loader
 .ClassAnnotator%7D
@@ -5609,12 +5609,14 @@
 ect.
-boot
+loader
 .Cla

Fix link to ClassAnnotator