001/* 002 * Copyright (C) 2022 - 2024, 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.ByteBuffer; 022import java.nio.charset.Charset; 023import java.nio.file.Path; 024import java.util.List; 025 026/** 027 * Chainable builder for creating individual files. 028 * 029 * @author Ashley Scopes 030 * @since 0.0.1 031 */ 032@SuppressWarnings({"unused", "UnusedReturnValue"}) 033public interface FileBuilder { 034 035 /** 036 * Take the directory, represented by the given {@link ManagedDirectory}, and convert it into a 037 * JAR file, that will be written to the file being created. 038 * 039 * @param directory the managed directory to use. 040 * @return the root managed directory for further configuration. 041 * @since 0.3.0 042 */ 043 ManagedDirectory asJarFrom(PathRoot directory); 044 045 /** 046 * Take the directory, represented by the given path, and convert it into a JAR file, that will be 047 * written to the file being created. 048 * 049 * <pre><code> 050 * ManagedDirectory someDirectory = ...; 051 * someDirectory 052 * .createFile("first.jar") 053 * .asJarFrom(firstClassOutputs); 054 * var secondCompilation = secondCompiler.compile(secondWorkspace); 055 * ... 056 * </code></pre> 057 * 058 * @param directory the directory to use. 059 * @return the root managed directory for further configuration. 060 * @since 0.3.0 061 */ 062 ManagedDirectory asJarFrom(Path directory); 063 064 /** 065 * Copy a resource from the given class loader into the file system. 066 * 067 * <pre><code> 068 * var classLoader = getClass().getClassLoader(); 069 * 070 * directory 071 * .createFile("org", "example", "HelloWorld.class") 072 * .copiedFromClassPath(classLoader, "org/example/HelloWorld.class"); 073 * </code></pre> 074 * 075 * @param classLoader the class loader to use. 076 * @param resource the resource to copy. 077 * @return the root managed directory for further configuration. 078 */ 079 ManagedDirectory copiedFromClassPath(ClassLoader classLoader, String resource); 080 081 /** 082 * Copy a resource from the class loader on the current thread into the file system. 083 * 084 * <pre><code> 085 * directory 086 * .createFile("org", "example", "HelloWorld.class") 087 * .copiedFromClassPath("org/example/HelloWorld.class"); 088 * </code></pre> 089 * 090 * @param resource the resource to copy. 091 * @return the root managed directory for further configuration. 092 */ 093 ManagedDirectory copiedFromClassPath(String resource); 094 095 /** 096 * Copy the contents from the given file into the file system. 097 * 098 * <pre><code> 099 * var file = new File("src/test/resources/code/HelloWorld.java"); 100 * 101 * directory 102 * .createFile("org", "example", "HelloWorld.java") 103 * .copiedFromFile(file); 104 * </code></pre> 105 * 106 * @param file the file to read. 107 * @return the root managed directory for further configuration. 108 */ 109 ManagedDirectory copiedFromFile(File file); 110 111 /** 112 * Copy the contents from the given path into the file system. 113 * 114 * <pre><code> 115 * var file = Path.of("src", "test", "resources", "code", "HelloWorld.java"); 116 * 117 * directory 118 * .createFile("org", "example", "HelloWorld.java") 119 * .copiedFromFile(file); 120 * </code></pre> 121 * 122 * @param file the file to read. 123 * @return the root managed directory for further configuration. 124 */ 125 ManagedDirectory copiedFromFile(Path file); 126 127 /** 128 * Copy the contents from the given URL into the file system. 129 * 130 * <pre><code> 131 * var url = URI.create("file:///code/org/example/HelloWorld.java").toURI(); 132 * 133 * directory 134 * .createFile("org", "example", "HelloWorld.java") 135 * .copiedFromUrl(url); 136 * </code></pre> 137 * 138 * @param url the URL to read. 139 * @return the root managed directory for further configuration. 140 */ 141 ManagedDirectory copiedFromUrl(URL url); 142 143 /** 144 * Copy the contents from the given input stream into the file system. 145 * 146 * <p>The input stream will be closed when reading completes. 147 * 148 * <pre><code> 149 * try (var is = getClass().getResourceAsStream("code/examples/HelloWorld.java")) { 150 * directory 151 * .createFile("org", "example", "HelloWorld.java") 152 * .fromInputStream(is); 153 * } 154 * </code></pre> 155 * 156 * @param inputStream the input stream to read. 157 * @return the root managed directory for further configuration. 158 */ 159 ManagedDirectory fromInputStream(InputStream inputStream); 160 161 /** 162 * Create the file but do not put anything in it. 163 * 164 * <p>The resultant file will be 0 bytes long. 165 * 166 * <pre><code> 167 * directory 168 * .createFile(".gitkeep") 169 * .thatIsEmpty(); 170 * </code></pre> 171 * 172 * @return the root managed directory for further configuration. 173 */ 174 ManagedDirectory thatIsEmpty(); 175 176 /** 177 * Create the file with the given byte contents. 178 * 179 * <pre><code> 180 * var classFile = new byte[]{ 181 * -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, 182 * 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 6, 183 * 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 9, 0, 8, 0, 9, 7, 0, 10, 12, 0, 11, 184 * 0, 12, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 185 * 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 186 * 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 8, 0, 14, 1, 0, 13, 72, 101, 108, 108, 187 * 111, 44, 32, 87, 111, 114, 108, 100, 33, 10, 0, 16, 0, 17, 7, 0, 18, 12, 0, 19, 0, 20, 188 * 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 189 * 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 190 * 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 7, 0, 22, 1, 0, 10, 191 * 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 192 * 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 4, 109, 97, 105, 193 * 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 194 * 105, 110, 103, 59, 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 195 * 0, 15, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 46, 106, 97, 118, 97, 0, 33, 0, 196 * 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, 197 * 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, 198 * 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, 199 * 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, 200 * 0, 0, 2, 0, 28 201 * }; 202 * 203 * directory 204 * .createFile("HelloWorld.class") 205 * .withContents(classFile); 206 * </code></pre> 207 * 208 * @param contents the bytes to write. 209 * @return the root managed directory for further configuration. 210 * @see #withContents(ByteBuffer) 211 */ 212 ManagedDirectory withContents(byte[] contents); 213 214 /** 215 * An overload of {@link #withContents(byte[])} that consumes a NIO byte buffer. 216 * 217 * @param buffer the byte buffer to consume. 218 * @return the managed directory. 219 * @since 4.0.0 220 */ 221 default ManagedDirectory withContents(ByteBuffer buffer) { 222 var array = new byte[buffer.remaining()]; 223 buffer.get(array); 224 return withContents(array); 225 } 226 227 /** 228 * Create the file with the given contents. 229 * 230 * <pre><code> 231 * directory 232 * .createFile("org", "example", "HelloWorld.java") 233 * .withContents(StandardCharsets.US_ASCII, List.of( 234 * "package org.example;", 235 * "", 236 * "public class HelloWorld {", 237 * " public static void main(String[] args) {", 238 * " System.out.println(\"Hello, World!\");", 239 * " }", 240 * "}" 241 * )); 242 * </code></pre> 243 * 244 * @param charset the character encoding to use. 245 * @param lines the lines to write. 246 * @return the root managed directory for further configuration. 247 * @see #withContents(Charset, String...) 248 * @see #withContents(String...) 249 * @see #withContents(List) 250 * @see #withContents(byte[]) 251 * @since 4.0.0 252 */ 253 ManagedDirectory withContents(Charset charset, List<String> lines); 254 255 /** 256 * Create the file with the given contents. 257 * 258 * <pre><code> 259 * directory 260 * .createFile("org", "example", "HelloWorld.java") 261 * .withContents(StandardCharsets.US_ASCII, """ 262 * package org.example; 263 * 264 * public class HelloWorld { 265 * public static void main(String[] args) { 266 * System.out.println("Hello, World!"); 267 * } 268 * } 269 * """); 270 * </code></pre> 271 * 272 * <p>If the Java language level of your tests does not support multi-line strings, you can 273 * alternatively pass each line of text to write as a separate string. These will be written to 274 * the file using line-feed {@code '\n'} separators. For example: 275 * 276 * <pre><code> 277 * directory 278 * .createFile("org", "example", "HelloWorld.java") 279 * .withContents( 280 * StandardCharsets.US_ASCII, 281 * "package org.example;", 282 * "", 283 * "public class HelloWorld {", 284 * " public static void main(String[] args) {", 285 * " System.out.println(\"Hello, World!\");", 286 * " }", 287 * "}" 288 * ); 289 * </code></pre> 290 * 291 * @param charset the character encoding to use. 292 * @param lines the lines to write. 293 * @return the root managed directory for further configuration. 294 * @see #withContents(List) 295 * @see #withContents(Charset, List) 296 * @see #withContents(String...) 297 * @see #withContents(byte[]) 298 */ 299 default ManagedDirectory withContents(Charset charset, String... lines) { 300 return withContents(charset, List.of(lines)); 301 } 302 303 /** 304 * Create the file with the given contents as UTF-8. 305 * 306 * <p>If you are using multi-line strings, an example of usage would be: 307 * 308 * <pre><code> 309 * directory 310 * .createFile("org", "example", "HelloWorld.java") 311 * .withContents(List.of( 312 * "package org.example;", 313 * "", 314 * "public class HelloWorld {", 315 * " public static void main(String[] args) {", 316 * " System.out.println(\"Hello, World!\");", 317 * " }", 318 * "}" 319 * )); 320 * </code></pre> 321 * 322 * @param lines the lines to write using the default charset. 323 * @return the root managed directory for further configuration. 324 * @see #withContents(Charset, String...) 325 * @see #withContents(String...) 326 * @see #withContents(Charset, List) 327 * @see #withContents(byte[]) 328 * @since 4.0.0 329 */ 330 ManagedDirectory withContents(List<String> lines); 331 332 /** 333 * Create the file with the given contents as UTF-8. 334 * 335 * <p>If you are using multi-line strings, an example of usage would be: 336 * 337 * <pre><code> 338 * directory 339 * .createFile("org", "example", "HelloWorld.java") 340 * .withContents(""" 341 * package org.example; 342 * 343 * public class HelloWorld { 344 * public static void main(String[] args) { 345 * System.out.println("Hello, World!"); 346 * } 347 * } 348 * """); 349 * </code></pre> 350 * 351 * <p>If the Java language level of your tests does not support multi-line strings, you can 352 * alternatively pass each line of text to write as a separate string. These will be written to 353 * the file using line-feed {@code '\n'} separators. For example: 354 * 355 * <pre><code> 356 * directory 357 * .createFile("org", "example", "HelloWorld.java") 358 * .withContents( 359 * "package org.example;", 360 * "", 361 * "public class HelloWorld {", 362 * " public static void main(String[] args) {", 363 * " System.out.println(\"Hello, World!\");", 364 * " }", 365 * "}" 366 * ); 367 * </code></pre> 368 * 369 * @param lines the lines to write using the default charset. 370 * @return the root managed directory for further configuration. 371 * @see #withContents(Charset, String...) 372 * @see #withContents(List) 373 * @see #withContents(Charset, List) 374 * @see #withContents(byte[]) 375 */ 376 default ManagedDirectory withContents(String... lines) { 377 return withContents(List.of(lines)); 378 } 379}