001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2017-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core;
020
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.List;
024import java.util.Map;
025import java.util.ServiceLoader;
026import java.util.function.Function;
027
028/**
029 * A component that collects all component factory services of 
030 * a given type and creates an instance of each.
031 * 
032 * @param <F> the component factory type
033 */
034public class ComponentCollector<F extends ComponentFactory>
035        extends Component {
036
037    private static final List<Map<Object, Object>> SINGLE_DEFAULT
038        = Arrays.asList(Collections.emptyMap());
039
040    /**
041     * Creates a new collector that collects the factories of the given 
042     * type and uses each to create an instance with this component's
043     * channel. Before the instance is created, the `matcher` 
044     * function is invoked with the name of the class of the component
045     * to be created as argument. The list of maps returned is
046     * used to create components, passing each element in the list
047     * as parameter to {@link ComponentFactory#create(Channel, Map)}
048     * 
049     * @param factoryClass the factory class
050     * @param componentChannel this component's channel
051     * @param matcher the matcher function
052     */
053    public ComponentCollector(
054            Class<F> factoryClass, Channel componentChannel,
055            Function<String, List<Map<Object, Object>>> matcher) {
056        super(componentChannel);
057        ServiceLoader<F> serviceLoader = ServiceLoader.load(factoryClass);
058        for (F factory : serviceLoader) {
059            List<Map<Object, Object>> configs = matcher.apply(
060                factory.componentType().getName());
061            for (Map<Object, Object> config : configs) {
062                factory.create(channel(), config).ifPresent(
063                    component -> attach(component));
064            }
065        }
066    }
067
068    /**
069     * Utility constructor that uses each factory to create a single
070     * instance, using an empty map as properties.
071     * 
072     * @param factoryClass the factory class
073     * @param componentChannel this component's channel
074     */
075    public ComponentCollector(
076            Class<F> factoryClass, Channel componentChannel) {
077        this(factoryClass, componentChannel, type -> SINGLE_DEFAULT);
078    }
079}