All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.iti.wakamiti.api.WakamitiContributors Maven / Gradle / Ivy

The newest version!
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */
package es.iti.wakamiti.api;


import es.iti.commons.jext.Extension;
import es.iti.commons.jext.ExtensionManager;
import es.iti.wakamiti.api.extensions.*;
import es.iti.wakamiti.api.util.Pair;
import es.iti.wakamiti.api.imconfig.Configurable;
import es.iti.wakamiti.api.imconfig.Configuration;
import es.iti.wakamiti.api.imconfig.ConfigurationFactory;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * Manages contributors and extension points in Wakamiti API.
 * It handles various types of contributors, such as StepContributors, PlanBuilders, etc.
 * Provides methods to retrieve contributors, create instances, and perform configuration.
 * Acts as a central manager for contributors in Wakamiti API.
 *
 * @author Luis Iñesta Gelabert - [email protected]
 * @author Maria Galbis Calomarde - [email protected]
 */
public class WakamitiContributors {

    private static final AtomicBoolean VERSION_WARNED = new AtomicBoolean(false);
    private final List stepContributors = new LinkedList<>();
    private ExtensionManager extensionManager = new ExtensionManager();

    public void setClassLoaders(ClassLoader... loaders) {
        this.extensionManager = new ExtensionManager(loaders);
    }

    /**
     * Retrieves all contributors of a specific type.
     *
     * @return A map containing contributor types and their
     * corresponding contributors.
     */
    public Map, List> allContributors() {
        Class[] contributorTypes = {
                ConfigContributor.class,
                DataTypeContributor.class,
                EventObserver.class,
                PlanBuilder.class,
                PlanTransformer.class,
                Reporter.class,
                ResourceType.class,
                StepContributor.class
        };
        Map, List> map = Stream.of(contributorTypes)
                .map(type -> new Pair<>(type, extensionManager.getExtensions(type)
                        .map(Contributor.class::cast)
                        .peek(this::checkVersion)
                        .collect(Collectors.toList()))
                )
                .collect(Collectors.toMap(Pair::key, Pair::value));
        map.get(StepContributor.class).addAll(stepContributors);
        return map;
    }

    /**
     * Retrieves a contributor of a specific type.
     *
     * @param contributorClass The class of the contributor to retrieve.
     * @param               The type of the contributor.
     * @return The contributor of the specified type.
     * @throws WakamitiException If the contributor is not found.
     */
    public  T getContributor(Class contributorClass) {
        return stepContributors.stream()
                .filter(c -> contributorClass.isAssignableFrom(c.getClass()))
                .map(contributorClass::cast)
                .findFirst()
                .orElseThrow(() -> new WakamitiException(String.format("Contributor [%s] not found", contributorClass)));
    }

    /**
     * Adds StepContributors to the list.
     *
     * @param contributors List of StepContributors to add.
     */
    public void addStepContributors(List contributors) {
        stepContributors.addAll(contributors);
    }

    public Stream eventObservers() {
        return extensionManager.getExtensions(EventObserver.class)
                .peek(this::checkVersion);
    }

    /**
     * Creates a PlanBuilder for the given ResourceType and configuration.
     *
     * @param resourceType  The ResourceType for which the PlanBuilder is created.
     * @param configuration The Configuration to be used for configuration.
     * @return Optional containing the PlanBuilder if available.
     */
    public Optional createPlanBuilderFor(
            ResourceType resourceType,
            Configuration configuration
    ) {
        Optional planBuilder = extensionManager
                .getExtensionThatSatisfy(PlanBuilder.class, planner -> planner.acceptResourceType(resourceType));
        planBuilder.ifPresent(this::checkVersion);
        planBuilder.ifPresent(builder -> configure(builder, configuration));
        return planBuilder;
    }

    /**
     * Retrieves a Stream of available ResourceType instances.
     *
     * @return Stream of available ResourceTypes.
     */
    public Stream> availableResourceTypes() {
        return extensionManager.getExtensions(ResourceType.class)
                .peek(this::checkVersion)
                .map(x -> (ResourceType) x);
    }

    /**
     * Retrieves an optional ResourceType instance by its name.
     *
     * @param name The name of the ResourceType to retrieve.
     * @return Optional containing the ResourceType with the specified
     * name, or empty if not found.
     */
    public Optional> resourceTypeByName(String name) {
        return availableResourceTypes().filter(
                resourceType -> resourceType.extensionMetadata().name().equals(name)
        ).findAny();
    }

    /**
     * Retrieves a stream of DataTypeContributor instances based on the
     * specified modules.
     *
     * @param modules The list of module names.
     * @return Stream of DataTypeContributor instances satisfying the
     * specified modules.
     */
    public Stream dataTypeContributors(List modules) {
        Predicate condition = extension -> modules.contains(extension.name());
        return extensionManager
                .getExtensionsThatSatisfyMetadata(DataTypeContributor.class, condition)
                .peek(this::checkVersion);
    }

    public Stream allDataTypeContributors() {
        return extensionManager.getExtensions(DataTypeContributor.class)
                .peek(this::checkVersion);
    }

    public Stream allLoaderContributors() {
        return extensionManager.getExtensions(LoaderContributor.class)
                .peek(this::checkVersion);
    }

    /**
     * Creates a list of StepContributor instances based on the specified
     * modules and configuration.
     *
     * @param modules       List of module names.
     * @param configuration Configuration to be applied.
     * @return List of StepContributor instances.
     */
    public List createStepContributors(
            List modules,
            Configuration configuration
    ) {
        Predicate condition = extension -> modules.contains(extension.name());
        return extensionManager
                .getExtensionsThatSatisfyMetadata(StepContributor.class, condition)
                .peek(this::checkVersion)
                .peek(c -> configure(c, configuration))
                .collect(Collectors.toList());
    }

    /**
     * Creates a list of all StepContributor instances with the specified
     * configuration.
     *
     * @param configuration Configuration to be applied.
     * @return List of StepContributor instances.
     */
    public List createAllStepContributors(Configuration configuration) {
        return extensionManager
                .getExtensions(StepContributor.class)
                .peek(this::checkVersion)
                .peek(c -> configure(c, configuration))
                .collect(Collectors.toList());
    }

    public Stream allStepContributorMetadata() {
        return extensionManager.getExtensionMetadata(StepContributor.class);
    }

    /**
     * Retrieves the configuration contributors for a specific contributor
     * type.
     *
     * @param          The contributor type.
     * @param contributor The contributor instance.
     * @return Stream of ConfigContributor instances for the given
     * contributor type.
     */
    @SuppressWarnings("unchecked")
    public  Stream> configuratorsFor(T contributor) {
        return extensionManager
                .getExtensionsThatSatisfy(ConfigContributor.class, c -> c.accepts(contributor))
                .peek(this::checkVersion)
                .map(c -> (ConfigContributor) c);
    }

    /**
     * Configures a contributor using the provided configuration.
     *
     * @param contributor   The contributor to configure.
     * @param configuration The configuration to apply.
     * @param            The type of the contributor.
     * @return The configured contributor.
     */
    public  T configure(T contributor, Configuration configuration) {
        if (contributor instanceof Configurable) {
            ((Configurable) contributor).configure(configuration);
        }
        configuratorsFor(contributor).forEach(configurator ->
                configurator.configurer().configure(contributor, configurator.defaultConfiguration().append(configuration)));
        return contributor;
    }

    public Stream planTransformers() {
        return extensionManager.getExtensions(PlanTransformer.class)
                .peek(this::checkVersion);
    }

    /**
     * Configures property resolvers with the provided configuration.
     *
     * @param configuration The configuration to use for property resolvers.
     */
    public void propertyResolvers(Configuration configuration) {
        extensionManager.getExtensions(PropertyEvaluator.class)
                .peek(this::checkVersion)
                .forEach(c -> configure(c, configuration));
    }

    public Stream reporters() {
        return extensionManager.getExtensions(Reporter.class)
                .peek(this::checkVersion);
    }

    public ExtensionManager extensionManager() {
        return extensionManager;
    }

    public Configuration globalDefaultConfiguration() {
        return extensionManager.getExtensions(ConfigContributor.class)
                .peek(this::checkVersion)
                .map(ConfigContributor::defaultConfiguration)
                .reduce(ConfigurationFactory.instance().empty(), Configuration::append);
    }

    /**
     * Checks the compatibility of a contributor's version with the core version.
     *
     * @param contributor The contributor to check.
     */
    private void checkVersion(Contributor contributor) {
        String coreVersion = WakamitiAPI.instance().version();
        Optional coreVersionOptional = Optional.ofNullable(coreVersion)
                .flatMap(this::extractVersion);
        coreVersionOptional.ifPresentOrElse(v ->
                        Optional.ofNullable(contributor.extensionMetadata().version())
                                .flatMap(this::extractVersion)
                                .filter(version -> v < version)
                                .ifPresent(version -> {
                                    String message = String.format(
                                            "Contributor '%s' is compatible with the minimal core version %s, but it is %s",
                                            contributor.extensionMetadata().name(), version, v);
                                    throw new UnsupportedClassVersionError(message);
                                }),
                () -> {
                    if (!VERSION_WARNED.getAndSet(true)) {
                        System.err.println("WARNING: Core version is not in correct format: " + coreVersion);
                    }
                }
        );
    }

    private Optional extractVersion(String version) {
        Matcher matcher = Pattern.compile("^(\\d++\\.\\d++)(\\.\\d++.*+)?$").matcher(version);
        if (matcher.find()) {
            return Optional.of(Double.valueOf(matcher.group(1)));
        }
        return Optional.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy