// RendererRegistry.java
//
// Created: February 2006
//
// This file is a component of the BioMoby project.
// Copyright Martin Senger (martin.senger@gmail.com).
//

package org.biomoby.service.dashboard.renderers;

import org.apache.commons.discovery.tools.Service;
import org.apache.commons.discovery.tools.SPInterface;
import org.apache.commons.discovery.resource.ClassLoaders;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

/**
 * A registry that maintains a list of all renderer service
 * providers. <p>
 *
 * The <code>RendererRegistry</code> should be accessed through the
 * instance obtained by calling
 * <code>RendererRegistry.instance()</code>.  This instance is
 * initialised with the SPI implementations available through the
 * current class path.
 *
 * @author <A HREF="mailto:martin.senger@gmail.com">Martin Senger</A>
 * (based on work by Matthew Pocock for <a
 * href="http://taverna.sf.net">Taverna</a>)
 * @version $Id: RendererRegistry.java,v 1.3 2006/02/20 05:51:11 senger Exp $
 */
public class RendererRegistry {

    private static org.apache.commons.logging.Log log =
	org.apache.commons.logging.LogFactory.getLog (RendererRegistry.class);

    private static RendererRegistry instance;
    private List renderers;

    /**************************************************************************
     *
     **************************************************************************/
    public static synchronized RendererRegistry instance() {
        if (instance == null) {
            instance = new RendererRegistry();
            instance.loadInstances (RendererRegistry.class.getClassLoader());
        }
        return instance;
    }

    /**************************************************************************
     *
     **************************************************************************/
    protected RendererRegistry() {
        renderers = new ArrayList();
    }

    /**************************************************************************
     * Load all Renderer implementations that are registered in the
     * given ClassLoader. <p>
     *
     * @param classLoader  a ClassLoader which will be searched
     **************************************************************************/
    public void loadInstances (ClassLoader classLoader) {

	log.info ("Loading renderers started.");
        SPInterface spiIF = new SPInterface (Renderer.class);
        ClassLoaders loaders = new ClassLoaders();
        loaders.put (classLoader);
        Enumeration spe = Service.providers (spiIF, loaders);
        while (spe.hasMoreElements()) { 
            Renderer spi = (Renderer) spe.nextElement();
            log.info ("Renderer '" + spi.getName() + "' loaded.");
            renderers.add (spi);
        }
	log.info ("Loading renderers finished.");
    }

    /**************************************************************************
     * Get a default renderer for given 'value' within given
     * 'criterion'. <p>
     *
     * @param criterion is a category of the value, e.g. "MIME-TYPE"
     * @param value is a value from the category criterion, e.g. "text/xml"
     * @return the first found renderer complying with given
     * value/criterion, or null if none such renderer is available
     **************************************************************************/
    public Renderer getRenderer (String criterion, Object value) {
        for (Iterator i = renderers.iterator(); i.hasNext(); ) {
            Renderer rend = (Renderer) i.next();
            if (rend.canHandle (criterion, value))
                return rend;
        }
        return null;
    }

    /**************************************************************************
     * Get all renderers for given 'value' within given
     * 'criterion'. <p>
     *
     * @param criterion is a category of the value, e.g. "MIME-TYPE"
     * @param value is a value from the category criterion, e.g. "text/xml"
     * @return a (possibly empty) list of matching Renderer instances
     **************************************************************************/
    public List getRenderers (String criterion, Object value) {

	if (log.isDebugEnabled())
	    log.debug ("Can handle: " + criterion + " = " + value + "?");

        List res = new ArrayList();
        for (Iterator i = renderers.iterator(); i.hasNext();) {
            Renderer rend = (Renderer) i.next();
            if (rend.canHandle (criterion, value)) {
		if (log.isDebugEnabled())
		    log.debug ("\tfound " + rend.getName());
                res.add (rend);
	    }
        }
        return res;
    }

    /**************************************************************************
     * Add a renderer to this registry.
     **************************************************************************/
    public void addRenderer (Renderer renderer) {
        renderers.add (renderer);
    }

    /**************************************************************************
     * Remove a renderer from this registry.
     **************************************************************************/
    public void removeRenderer (Renderer renderer) {
        renderers.remove(renderer);
    }

    /**************************************************************************
     * How many renderers are available.
     **************************************************************************/
    public int size() {
        return renderers.size();
    }

    /**************************************************************************
     * Return an iterator over all registered renderers.
     **************************************************************************/
    public Iterator iterator() {
        return renderers.iterator();
    }

    /**************************************************************************
     * Get index-th renderer.
     **************************************************************************/
    public Renderer get (int index) {
        return (Renderer) renderers.get (index);
    }
}
