package org.jbehave.container.pico;

import static java.text.MessageFormat.format;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;

import org.jbehave.container.ComponentNotFoundException;
import org.jbehave.container.Container;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.PicoContainer;
import org.picocontainer.script.ScriptedContainerBuilder;

/**
 * <p>
 * Abstract implementation of Container which uses a PicoContainer as delegate
 * container.
 * </p>
 * 
 * @author Mauro Talevi
 */
public abstract class AbstractPicoContainer implements Container {

    private final ClassLoader classLoader;
    private final PicoContainer container;

    protected AbstractPicoContainer(String resource) {
        this(resource, Thread.currentThread().getContextClassLoader());
    }

    protected AbstractPicoContainer(String resource, ClassLoader classLoader) {
        this.classLoader = classLoader;
        this.container = buildContainer(resource);
    }

    public <T> T getComponent(Class<T> type) {
        return getPicoComponent(type, null);
    }

    public <T> T getComponent(Class<T> type, Object key) {
        return getPicoComponent(type, key);
    }

    public <T> Collection<Object> getComponentKeys(Class<T> type) {
        List<ComponentAdapter<T>> adapters = container.getComponentAdapters(type);
        List<Object> keys = new ArrayList<Object>();
        for (ComponentAdapter<T> adapter : adapters) {
            keys.add(adapter.getComponentKey());
        }
        return keys;
    }

    /**
     * Returns an instance of a component of a given type and key from the
     * delegate PicoContainer.
     * 
     * @param type the component Class
     * @param key the component key
     * @return A component instance for the given type and key, if provided
     * @throws NoComponentOfTypeException if no Component can be found of type
     * @throws ComponentNotFoundException if Component not found for key
     */
    private <T> T getPicoComponent(Class<T> type, Object key) {
        List<ComponentAdapter<T>> adapters = container.getComponentAdapters(type);
        if (adapters.isEmpty()) {
            String message = format("No component registered in container of type {0}", type);
            throw new ComponentNotFoundException(message);
        }
        if (key != null) {
            // a key has been provided: return the component for that key
            for (ComponentAdapter<T> adapter : adapters) {
                if (key.equals(adapter.getComponentKey())) {
                    return adapter.getComponentInstance(container, type);
                }
            }
            String message = format("No component registered in container of type {0} and for key {1}", type, key);
            throw new ComponentNotFoundException(message);
        } else {
            // no key has been found:
            // return first of registered components
            return adapters.iterator().next().getComponentInstance(container, type);
        }
    }

    /**
     * Builds PicoContainer from a given resource
     * 
     * @param resource the String encoding the script path
     * @return A PicoContainer
     */
    private PicoContainer buildContainer(String resource) {
        Reader script = getReader(resource, classLoader);
        ScriptedContainerBuilder builder = createContainerBuilder(script, classLoader);
        return builder.buildContainer(null, null, true);
    }

    private Reader getReader(String resource, ClassLoader classLoader) {
        InputStream is = classLoader.getResourceAsStream(resource);
        if (is == null) {
            String message = format("Resource {0} not found in ClassLoader {1}", resource, classLoader.getClass(),
                    classLoader);
            throw new NoSuchElementException(message);
        }
        return new InputStreamReader(is);
    }

    /**
     * Allow concrete implementations to specify a ScriptedContainerBuilder
     * 
     * @param script the Reader containing the container script
     * @param classLoader the ClassLoader
     * @return A ScriptedContainerBuilder
     */
    protected abstract ScriptedContainerBuilder createContainerBuilder(Reader script, ClassLoader classLoader);

}

@@ -166,65 +166,8 @@
 er;%0A
-import java.util.ArrayList;%0Aimport java.util.Collection;%0A
 impo
@@ -185,16 +185,16 @@
 l.List;%0A
+
 import j
@@ -1134,36 +1134,32 @@
       return get
-Pico
 Component(type, 
@@ -1235,902 +1235,8 @@
 ) %7B%0A
-        return getPicoComponent(type, key);%0A    %7D%0A%0A    public %3CT%3E Collection%3CObject%3E getComponentKeys(Class%3CT%3E type) %7B%0A        List%3CComponentAdapter%3CT%3E%3E adapters = container.getComponentAdapters(type);%0A        List%3CObject%3E keys = new ArrayList%3CObject%3E();%0A        for (ComponentAdapter%3CT%3E adapter : adapters) %7B%0A            keys.add(adapter.getComponentKey());%0A        %7D%0A        return keys;%0A    %7D%0A%0A    /**%0A     * Returns an instance of a component of a given type and key from the%0A     * delegate PicoContainer.%0A     * %0A     * @param type the component Class%0A     * @param key the component key%0A     * @return A component instance for the given type and key, if provided%0A     * @throws NoComponentOfTypeException if no Component can be found of type%0A     * @throws ComponentNotFoundException if Component not found for key%0A     */%0A    private %3CT%3E T getPicoComponent(Class%3CT%3E type, Object key) %7B%0A
     
@@ -2228,172 +2228,8 @@
  %7D%0A%0A
-    /**%0A     * Builds PicoContainer from a given resource%0A     * %0A     * @param resource the String encoding the script path%0A     * @return A PicoContainer%0A     */%0A
     
@@ -2484,11 +2484,12 @@
 ll, 
-tru
+fals
 e);%0A

Removed redundant method.