001/* 002 * JDrupes Builder 003 * Copyright (C) 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 java.util.EnumSet; 023import java.util.Set; 024import java.util.concurrent.atomic.AtomicReference; 025import java.util.function.Consumer; 026import java.util.function.Predicate; 027import java.util.stream.Stream; 028import org.jdrupes.builder.api.Intent; 029import static org.jdrupes.builder.api.Intent.Consume; 030import static org.jdrupes.builder.api.Intent.Expose; 031import static org.jdrupes.builder.api.Intent.Supply; 032import org.jdrupes.builder.api.Project; 033import org.jdrupes.builder.api.ProviderSelection; 034import org.jdrupes.builder.api.Resource; 035import org.jdrupes.builder.api.ResourceProvider; 036import org.jdrupes.builder.api.ResourceRequest; 037 038/// The Class DefaultBoundResourceQuery. 039/// 040public class DefaultProviderSelection implements ProviderSelection { 041 042 private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 043 private final AbstractProject project; 044 private final Set<Intent> preSelected; 045 private Predicate<ResourceProvider> filter = _ -> true; 046 private Consumer<ResourceProvider> onBeforeUse = _ -> { 047 }; 048 049 /* default */ DefaultProviderSelection(AbstractProject project) { 050 this.project = project; 051 this.preSelected = null; 052 } 053 054 /* default */ DefaultProviderSelection(AbstractProject project, 055 Set<Intent> intends) { 056 this.project = project; 057 this.preSelected = intends; 058 } 059 060 @Override 061 public ProviderSelection filter(Predicate<ResourceProvider> filter) { 062 this.filter = this.filter.and(filter); 063 return this; 064 } 065 066 @Override 067 public DefaultProviderSelection without(ResourceProvider provider) { 068 filter = filter.and(p -> !p.equals(provider)); 069 return this; 070 } 071 072 @Override 073 public DefaultProviderSelection 074 without(Class<? extends ResourceProvider> providerType) { 075 filter = filter.and(p -> !providerType.isAssignableFrom(p.getClass())); 076 return this; 077 } 078 079 @Override 080 public DefaultProviderSelection 081 onBeforeUse(Consumer<ResourceProvider> hook) { 082 this.onBeforeUse = hook; 083 return this; 084 } 085 086 @Override 087 public Stream<ResourceProvider> select(Set<Intent> intents) { 088 if (preSelected != null) { 089 if (!intents.isEmpty()) { 090 throw new IllegalArgumentException( 091 "Duplicates selection of intents."); 092 } 093 intents = preSelected; 094 } 095 return project.dependencies(intents).filter(filter) 096 .peek(onBeforeUse::accept); 097 } 098 099 @Override 100 public <T extends Resource> Stream<T> 101 resources(ResourceRequest<T> request) { 102 AtomicReference<ResourceRequest<T>> projectRequest 103 = new AtomicReference<>(); 104 var providerStream = select(request.uses()).map(p -> { 105 if (p instanceof Project) { 106 logger.atFinest() 107 .log("%s forwards % to %s", project, request, p); 108 return project.context().resources(p, 109 projectRequest.updateAndGet( 110 r -> r != null ? r : forwardedRequest(request))); 111 } 112 return project.context().resources(p, request); 113 }); 114 if (project.providersUnlocked()) { 115 // If accessing providers is allowed, get the providers 116 // eagerly to trigger evaluation. Resources are still 117 // obtained lazily. 118 providerStream = providerStream.toList().stream(); 119 } 120 return providerStream.flatMap(s -> s); 121 } 122 123 private <T extends Resource> ResourceRequest<T> 124 forwardedRequest(ResourceRequest<T> requested) { 125 Set<Intent> uses = preSelected != null ? preSelected : requested.uses(); 126 Set<Intent> mapped = EnumSet.copyOf(uses); 127 if (uses.stream().filter( 128 EnumSet.of(Consume, Expose)::contains).findAny().isPresent()) { 129 mapped.remove(Consume); 130 mapped.addAll(EnumSet.of(Supply, Expose)); 131 } 132 return requested.using(mapped); 133 } 134}