/**
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 * Copyright (C)
 * <a href="http://www.inab.org">Spanish National Institute of Bioinformatics (INB)</a>
 * <a href="http://www.bsc.es">Barcelona Supercomputing Center (BSC)</a>
 * <a href="http://inb.bsc.es">Computational Node 6</a>
 */

package org.inb.biomoby.central;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

/**
 * @author Dmitry Repchevsky
 */

public class ServiceLoader<T> implements Iterable<T>
{
    private final static String LOCATION = "META-INF/services/";

    private static Map<String, ServiceLoader> map;

    private List<T> services;

    private ServiceLoader(List<T> services)
    {
        this.services = services;
    }

    public static synchronized <T> ServiceLoader<T> load(Class<T> clazz)
    {
        String name = clazz.getName();

        ServiceLoader loader;

        if (map == null)
        {
            map = new TreeMap<String, ServiceLoader>();
        }
        else
        {
            loader = map.get(name);

            if (loader != null)
            {
                return loader;
            }
        }

        List<T> services = new ArrayList<T>();
        loader = new ServiceLoader<T>(services);

        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        InputStream in = cl.getResourceAsStream(LOCATION + name);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        try
        {
            String line;
            while((line = reader.readLine()) != null)
            {
                int comment = line.indexOf('#');

                if (comment >= 0)
                {
                    line = line.substring(0, comment);
                }

                line = line.trim();
                
                if (line.length() > 0)
                {
                    Class<?> clz = Class.forName(line);
                    Class<? extends T> impl = clz.asSubclass(clazz);
                    Constructor<? extends T> ctor = impl.getConstructor();
                    T svc = ctor.newInstance();
                    services.add(svc);

                }
            }

            map.put(name, loader);
        }
        catch(Exception ex)
        {
            Logger.getLogger(ServiceLoader.class.getCanonicalName()).severe("Can't find a service: " + clazz.getCanonicalName());
        }
        finally
        {
            try { in.close(); }
            catch(IOException ex) {}
        }

        return loader;
    }

    public Iterator<T> iterator()
    {
        return services.iterator();
    }
}
