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