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