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.core;
020
021import com.google.common.flogger.FluentLogger;
022import static com.google.common.flogger.StackSize.*;
023import java.util.stream.Stream;
024import org.jdrupes.builder.api.Renamable;
025import org.jdrupes.builder.api.Resource;
026import org.jdrupes.builder.api.ResourceFactory;
027import org.jdrupes.builder.api.ResourceProvider;
028import org.jdrupes.builder.api.ResourceProviderSpi;
029import org.jdrupes.builder.api.ResourceRequest;
030import org.jdrupes.builder.api.ResourceType;
031import org.jdrupes.builder.api.Resources;
032
033/// A base implementation for[ResourceProvider]s.
034///
035public abstract class AbstractProvider implements ResourceProvider {
036
037    private static final FluentLogger logger = FluentLogger.forEnclosingClass();
038    private String name;
039
040    /// Initializes a new abstract provider.
041    ///
042    public AbstractProvider() {
043        // Default name
044        name = getClass().getSimpleName();
045        if (name.isBlank()) {
046            name = "Adapted " + getClass().getSuperclass().getSimpleName();
047        }
048    }
049
050    @Override
051    public String name() {
052        return name;
053    }
054
055    /// Allows derived classes to change the provider's name in their
056    /// implementation of [Renamable#name]. Throws an
057    /// [UnsupportedOperationException] if the derived class does not
058    /// implement [Renamable].
059    ///
060    /// @param name the name
061    /// @return the abstract provider
062    ///
063    protected final AbstractProvider rename(String name) {
064        if (!(this instanceof Renamable)) {
065            throw new UnsupportedOperationException(getClass().getName()
066                + " does not implement " + Renamable.class.getName());
067        }
068        this.name = name;
069        return this;
070    }
071
072    /* default */ ResourceProviderSpi toSpi() {
073        return new ResourceProviderSpi() {
074            @Override
075            public <T extends Resource> Stream<T>
076                    provide(ResourceRequest<T> requested) {
077                if (!FutureStream.isProviderInvocationAllowed()) {
078                    logger.atWarning().withStackTrace(MEDIUM)
079                        .log("Direct invocation of %s is not allowed", this);
080                }
081                return doProvide(requested);
082            }
083        };
084    }
085
086    /// Invoked by [ResourceProviderSpi#provide] after checking if the
087    /// invocation is allowed.
088    ///
089    /// @param <T> the generic type
090    /// @param requested the requested
091    /// @return the stream
092    ///
093    protected abstract <T extends Resource> Stream<T>
094            doProvide(ResourceRequest<T> requested);
095
096    @Override
097    @SuppressWarnings("PMD.ShortMethodName")
098    public <T extends Resource> ResourceRequest<T>
099            of(ResourceType<? extends T> type) {
100        return new DefaultResourceRequest<>(type);
101    }
102
103    @Override
104    public DefaultBuildContext context() {
105        return LauncherSupport.buildContext();
106    }
107
108    /// Convenience function to create a new resource. Short for invoking
109    /// [ResourceFactory#create] with the requested resource type,
110    /// `null` for the project and the remaining given arguments appended.
111    ///
112    /// @param <T> the resource type
113    /// @param type the type
114    /// @param args the arguments
115    /// @return the resource
116    ///
117    protected <T extends Resource> T newResource(ResourceType<T> type,
118            Object... args) {
119        return ResourceFactory.create(type, null, args);
120    }
121
122    /// Retrieves the resources as a Vavr stream.
123    ///
124    /// @param <T> the resource type
125    /// @param resources the resources
126    /// @return the stream
127    ///
128    public <T extends Resource> io.vavr.collection.Stream<T>
129            vavrStream(Resources<T> resources) {
130        return io.vavr.collection.Stream.ofAll(resources.stream());
131    }
132
133    @Override
134    public String toString() {
135        return "Provider " + getClass().getSimpleName();
136    }
137}