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.io.InputStream; 022import java.io.OutputStream; 023import java.nio.file.Path; 024import java.util.Objects; 025import java.util.function.BiConsumer; 026import java.util.stream.Stream; 027import org.jdrupes.builder.api.FileTree; 028import org.jdrupes.builder.api.Project; 029import org.jdrupes.builder.api.Resource; 030import org.jdrupes.builder.api.ResourceRequest; 031import org.jdrupes.builder.api.ResourceType; 032import static org.jdrupes.builder.api.ResourceType.CleanlinessType; 033import org.jdrupes.builder.api.Resources; 034 035/// The Class Copier. 036/// 037public class Copier extends AbstractGenerator { 038 039 /// A source description. 040 /// 041 /// @param tree the tree to copy 042 /// @param subdir a subdirectory of the destination set with [#into] 043 /// @param filter the filter 044 /// 045 public record Source(FileTree<?> tree, Path subdir, 046 BiConsumer<InputStream, OutputStream> filter) { 047 } 048 049 private final StreamCollector<Source> sources 050 = new StreamCollector<>(true); 051 private Path destination; 052 private ResourceRequest<?> requestForCopied; 053 054 /// Initializes a new copier. 055 /// 056 /// @param project the project 057 /// 058 public Copier(Project project) { 059 super(project); 060 } 061 062 /// Add the given [Stream] of Sources to the source resources. 063 /// 064 /// @param sources the sources 065 /// @return the copier 066 /// 067 public Copier source(Stream<Source> sources) { 068 this.sources.add(sources); 069 return this; 070 } 071 072 /// Convenience method to add a [FileTree] without subdirectory or 073 /// filter to the sources. If `root` is a relative path, it is resolved 074 /// against the project's directory. 075 /// 076 /// @param root the root 077 /// @param pattern the pattern 078 /// @return the copier 079 /// 080 public Copier source(Path root, String pattern) { 081 sources.add(Stream.of(new Source(FileTree.from( 082 project(), root, pattern), Path.of("."), null))); 083 return this; 084 } 085 086 /// Sets the destination directory for the copied resources. If the 087 /// destination is relative, it is resolved against the project's 088 /// directory. 089 /// 090 /// @param destination the destination 091 /// @return the copier 092 /// 093 public Copier into(Path destination) { 094 if (!destination.isAbsolute()) { 095 destination = project().directory().resolve(destination); 096 } 097 if (!destination.toFile().isDirectory()) { 098 throw new IllegalArgumentException("Argument must be a directory."); 099 } 100 this.destination = destination; 101 return this; 102 } 103 104 /// Provide the generated resources in response to a request like 105 /// the given one. 106 /// 107 /// @param proto defines the kind of request that the copier 108 /// should respond to with the copied resources 109 /// @return the copier 110 /// 111 public Copier provideResources(ResourceRequest<?> proto) { 112 requestForCopied = proto; 113 return this; 114 } 115 116 @Override 117 protected <T extends Resource> Stream<T> 118 doProvide(ResourceRequest<T> request) { 119 if (request.accepts(CleanlinessType)) { 120 // TODO 121 return Stream.empty(); 122 } 123 124 // Handle request for generated resources 125 if (requestForCopied != null 126 && request.accepts(requestForCopied.type()) 127 && (requestForCopied.name().isEmpty() 128 || Objects.equals(requestForCopied.name().get(), 129 request.name().orElse(null)))) { 130 // Always evaluate for most special type 131 if (!request.equals(requestForCopied)) { 132 @SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" }) 133 var result = (Stream<T>) resources(requestForCopied); 134 return result; 135 } 136 } 137 138 if (destination == null) { 139 throw new IllegalStateException("No destination set."); 140 } 141 142 // Make sure that the required resources are retrieved and exist 143 var required = sources.stream().toList(); 144 145 return Stream.empty(); 146 } 147 148}