001/* 002 * Copyright (C) 2022 - 2023, the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.github.ascopes.jct.workspaces; 017 018import java.io.File; 019import java.io.InputStream; 020import java.net.URL; 021import java.nio.charset.Charset; 022import java.nio.file.Path; 023import org.apiguardian.api.API; 024import org.apiguardian.api.API.Status; 025 026/** 027 * Chainable builder for creating individual files. 028 * 029 * @author Ashley Scopes 030 * @since 0.0.1 031 */ 032@API(since = "0.0.1", status = Status.STABLE) 033@SuppressWarnings("unused") 034public interface FileBuilder { 035 036 /** 037 * Take the directory, represented by the given {@link ManagedDirectory}, and convert it into a 038 * JAR file, that will be written to the file being created. 039 * 040 * <pre><code> 041 * // Compile some files first. 042 * Workspace firstWorkspace = ...; 043 * JctCompilation firstCompilation = firstCompiler.compile(firstWorkspace); 044 * ManagedDirectory firstClassOutputs = firstWorkspace.getClassOutputPackage(); 045 * 046 * // Wrap the first compilation outputs in a JAR and add as a class path dependency 047 * // to the second compilation. 048 * Workspace secondWorkspace = ...; 049 * secondWorkspace 050 * .createFile("first.jar") 051 * .asJarFrom(firstClassOutputs); 052 * JctCompilation secondCompilation = secondCompiler.compile(secondWorkspace); 053 * ... 054 * </code></pre> 055 * 056 * @param directory the managed directory to use. 057 * @return the root managed directory for further configuration. 058 */ 059 @API(since = "0.3.0", status = Status.STABLE) 060 ManagedDirectory asJarFrom(ManagedDirectory directory); 061 062 /** 063 * Take the directory, represented by the given path, and convert it into a JAR file, that will be 064 * written to the file being created. 065 * 066 * <pre><code> 067 * ManagedDirectory someDirectory = ...; 068 * someDirectory 069 * .createFile("first.jar") 070 * .asJarFrom(firstClassOutputs); 071 * var secondCompilation = secondCompiler.compile(secondWorkspace); 072 * ... 073 * </code></pre> 074 * 075 * @param directory the directory to use. 076 * @return the root managed directory for further configuration. 077 */ 078 @API(since = "0.3.0", status = Status.STABLE) 079 ManagedDirectory asJarFrom(Path directory); 080 081 /** 082 * Copy a resource from the given class loader into the file system. 083 * 084 * <pre><code> 085 * var classLoader = getClass().getClassLoader(); 086 * 087 * directory 088 * .createFile("org", "example", "HelloWorld.class") 089 * .copiedFromClassPath(classLoader, "org/example/HelloWorld.class"); 090 * </code></pre> 091 * 092 * @param classLoader the class loader to use. 093 * @param resource the resource to copy. 094 * @return the root managed directory for further configuration. 095 */ 096 ManagedDirectory copiedFromClassPath(ClassLoader classLoader, String resource); 097 098 /** 099 * Copy a resource from the class loader on the current thread into the file system. 100 * 101 * <pre><code> 102 * directory 103 * .createFile("org", "example", "HelloWorld.class") 104 * .copiedFromClassPath("org/example/HelloWorld.class"); 105 * </code></pre> 106 * 107 * @param resource the resource to copy. 108 * @return the root managed directory for further configuration. 109 */ 110 ManagedDirectory copiedFromClassPath(String resource); 111 112 /** 113 * Copy the contents from the given file into the file system. 114 * 115 * <pre><code> 116 * var file = new File("src/test/resources/code/HelloWorld.java"); 117 * 118 * directory 119 * .createFile("org", "example", "HelloWorld.java") 120 * .copiedFromFile(file); 121 * </code></pre> 122 * 123 * @param file the file to read. 124 * @return the root managed directory for further configuration. 125 */ 126 ManagedDirectory copiedFromFile(File file); 127 128 /** 129 * Copy the contents from the given path into the file system. 130 * 131 * <pre><code> 132 * var file = Path.of("src", "test", "resources", "code", "HelloWorld.java"); 133 * 134 * directory 135 * .createFile("org", "example", "HelloWorld.java") 136 * .copiedFromFile(file); 137 * </code></pre> 138 * 139 * @param file the file to read. 140 * @return the root managed directory for further configuration. 141 */ 142 ManagedDirectory copiedFromFile(Path file); 143 144 /** 145 * Copy the contents from the given URL into the file system. 146 * 147 * <pre><code> 148 * var url = URI.create("file:///code/org/example/HelloWorld.java").toURI(); 149 * 150 * directory 151 * .createFile("org", "example", "HelloWorld.java") 152 * .copiedFromUrl(url); 153 * </code></pre> 154 * 155 * @param url the URL to read. 156 * @return the root managed directory for further configuration. 157 */ 158 ManagedDirectory copiedFromUrl(URL url); 159 160 /** 161 * Copy the contents from the given input stream into the file system. 162 * 163 * <p>The input stream will be closed when reading completes. 164 * 165 * <pre><code> 166 * try (var is = getClass().getResourceAsStream("code/examples/HelloWorld.java")) { 167 * directory 168 * .createFile("org", "example", "HelloWorld.java") 169 * .fromInputStream(is); 170 * } 171 * </code></pre> 172 * 173 * @param inputStream the input stream to read. 174 * @return the root managed directory for further configuration. 175 */ 176 ManagedDirectory fromInputStream(InputStream inputStream); 177 178 /** 179 * Create the file but do not put anything in it. 180 * 181 * <p>The resultant file will be 0 bytes long. 182 * 183 * <pre><code> 184 * directory 185 * .createFile(".gitkeep") 186 * .thatIsEmpty(); 187 * </code></pre> 188 * 189 * @return the root managed directory for further configuration. 190 */ 191 ManagedDirectory thatIsEmpty(); 192 193 194 /** 195 * Create the file with the given byte contents. 196 * 197 * <pre><code> 198 * var classFile = new byte[]{ 199 * -54, -2, -70, -66, 0, 0, 0, 63, 0, 29, 10, 0, 2, 0, 3, 7, 0, 4, 12, 0, 5, 0, 6, 1, 0, 200 * 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 6, 201 * 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 9, 0, 8, 0, 9, 7, 0, 10, 12, 0, 11, 202 * 0, 12, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 203 * 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 204 * 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 8, 0, 14, 1, 0, 13, 72, 101, 108, 108, 205 * 111, 44, 32, 87, 111, 114, 108, 100, 33, 10, 0, 16, 0, 17, 7, 0, 18, 12, 0, 19, 0, 20, 206 * 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 207 * 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 208 * 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 7, 0, 22, 1, 0, 10, 209 * 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 210 * 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 4, 109, 97, 105, 211 * 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 212 * 105, 110, 103, 59, 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 213 * 0, 15, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 46, 106, 97, 118, 97, 0, 33, 0, 214 * 21, 0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 23, 0, 0, 0, 29, 0, 1, 0, 1, 0, 215 * 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 24, 0, 0, 0, 6, 0, 1, 0, 0, 0, 1, 0, 9, 0, 216 * 25, 0, 26, 0, 1, 0, 23, 0, 0, 0, 37, 0, 2, 0, 1, 0, 0, 0, 9, -78, 0, 7, 18, 13, -74, 0, 217 * 15, -79, 0, 0, 0, 1, 0, 24, 0, 0, 0, 10, 0, 2, 0, 0, 0, 3, 0, 8, 0, 4, 0, 1, 0, 27, 0, 218 * 0, 0, 2, 0, 28 219 * }; 220 * 221 * directory 222 * .createFile("HelloWorld.class") 223 * .withContents(classFile); 224 * </code></pre> 225 * 226 * @param contents the bytes to write. 227 * @return the root managed directory for further configuration. 228 */ 229 ManagedDirectory withContents(byte[] contents); 230 231 /** 232 * Create the file with the given contents. 233 * 234 * <pre><code> 235 * directory 236 * .createFile("org", "example", "HelloWorld.java") 237 * .withContents(StandardCharsets.US_ASCII, """ 238 * package org.example; 239 * 240 * public class HelloWorld { 241 * public static void main(String[] args) { 242 * System.out.println("Hello, World!"); 243 * } 244 * } 245 * """); 246 * </code></pre> 247 * 248 * <p>If the Java language level of your tests does not support multi-line strings, you can 249 * alternatively pass each line of text to write as a separate string. These will be written to 250 * the file using line-feed {@code '\n'} separators. For example: 251 * 252 * <pre><code> 253 * directory 254 * .createFile("org", "example", "HelloWorld.java") 255 * .withContents( 256 * StandardCharsets.US_ASCII, 257 * "package org.example;", 258 * "", 259 * "public class HelloWorld {", 260 * " public static void main(String[] args) {", 261 * " System.out.println(\"Hello, World!\");", 262 * " }", 263 * "}" 264 * ); 265 * </code></pre> 266 * 267 * @param charset the character encoding to use. 268 * @param lines the lines to write. 269 * @return the root managed directory for further configuration. 270 * @see #withContents(String...) 271 * @see #withContents(byte[]) 272 */ 273 ManagedDirectory withContents(Charset charset, String... lines); 274 275 /** 276 * Create the file with the given contents as UTF-8. 277 * 278 * <p>If you are using multi-line strings, an example of usage would be: 279 * 280 * <pre><code> 281 * directory 282 * .createFile("org", "example", "HelloWorld.java") 283 * .withContents(""" 284 * package org.example; 285 * 286 * public class HelloWorld { 287 * public static void main(String[] args) { 288 * System.out.println("Hello, World!"); 289 * } 290 * } 291 * """); 292 * </code></pre> 293 * 294 * <p>If the Java language level of your tests does not support multi-line strings, you can 295 * alternatively pass each line of text to write as a separate string. These will be written to 296 * the file using line-feed {@code '\n'} separators. For example: 297 * 298 * <pre><code> 299 * directory 300 * .createFile("org", "example", "HelloWorld.java") 301 * .withContents( 302 * "package org.example;", 303 * "", 304 * "public class HelloWorld {", 305 * " public static void main(String[] args) {", 306 * " System.out.println(\"Hello, World!\");", 307 * " }", 308 * "}" 309 * ); 310 * </code></pre> 311 * 312 * @param lines the lines to write using the default charset. 313 * @return the root managed directory for further configuration. 314 * @see #withContents(Charset, String...) 315 * @see #withContents(byte[]) 316 */ 317 ManagedDirectory withContents(String... lines); 318}