001/*
002 * JDrupes Builder
003 * Copyright (C) 2025 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.api;
020
021import java.util.Optional;
022import java.util.ServiceLoader;
023import java.util.stream.StreamSupport;
024
025/// Defines both an interface for factories that create [Resource]s and
026/// factory methods for invoking an appropriate factory.
027///
028@SuppressWarnings("PMD.ImplicitFunctionalInterface")
029public interface ResourceFactory {
030
031    /// Returns a new resource with the given type, passing the given
032    /// arguments to the constructor of the resource. The implementation
033    /// uses [ServiceLoader] to find a [ResourceFactory] that creates the
034    /// resource, i.e. that does not return `Optional.empty()` when
035    /// [newResource] is called.
036    ///
037    /// @param <T> the generic resource type
038    /// @param type the resource type
039    /// @param project the project
040    /// @param args the additional arguments
041    /// @return the resource
042    ///
043    static <T extends Resource> T create(ResourceType<T> type,
044            Project project, Object... args) {
045        return StreamSupport.stream(
046            ServiceLoader.load(ResourceFactory.class).spliterator(), true)
047            .map(f -> f.newResource(type, project, args))
048            .filter(Optional::isPresent).map(Optional::get).findFirst()
049            .orElseThrow(
050                () -> new BuildException("No resource factory for " + type));
051    }
052
053    /// Short for `create(type, null, args)`.
054    ///
055    /// @param <T> the generic resource type
056    /// @param type the resource type
057    /// @param args the additional arguments
058    /// @return the resource
059    ///
060    static <T extends Resource> T create(ResourceType<T> type,
061            Object... args) {
062        return create(type, null, args);
063    }
064
065    /// Returns a new resource of the given type if the factory instance
066    /// can create it.
067    ///
068    /// @param <T> the generic resource type
069    /// @param type the resource type
070    /// @param project the project
071    /// @param args the additional arguments
072    /// @return the result. `Optional.empty()` if the resource cannot
073    /// be created by the factory instance.
074    ///
075    <T extends Resource> Optional<T> newResource(ResourceType<T> type,
076            Project project, Object... args);
077
078}