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