001/* 002 * Copyright (C) 2022 - 2025, 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 io.github.ascopes.jct.filemanagers.JctFileManager; 019import io.github.ascopes.jct.filemanagers.ModuleLocation; 020import java.io.UncheckedIOException; 021import java.nio.file.Path; 022import java.util.List; 023import java.util.Map; 024import javax.tools.JavaFileManager.Location; 025import javax.tools.StandardLocation; 026 027/** 028 * Interface for a Workspace to hold files and directories within. 029 * 030 * <p>This acts as a nexus for managing the lifetime of test sources and directories, 031 * and should be used within a try-with-resources block to ensure temporary files get released after 032 * the test completes. 033 * 034 * <p>While this interface may seem somewhat intimidating due to the number of methods it provides, 035 * you will usually only ever need to use a small subset of them. The main ones you probably will 036 * want to use are: 037 * 038 * <ul> 039 * <li> 040 * {@link #addSourcePathPackage(Path)} - for copying a source path tree from your file system. 041 * </li> 042 * <li> 043 * {@link #createSourcePathPackage()} - for creating a new source path tree. 044 * </li> 045 * <li> 046 * {@link #addClassPathPackage(Path)} - for adding a class path resource (usually a JAR or a 047 * directory of packages of classes). 048 * </li> 049 * <li> 050 * {@link #addModulePathModule(String, Path)} - for adding a module path resource (usually a JAR 051 * or a directory of packages of classes). 052 * </li> 053 * <li> 054 * {@link #getClassPathPackages()} - for fetching all class path resources. 055 * </li> 056 * <li> 057 * {@link #getModulePathModules()} - for fetching all module path resources. 058 * </li> 059 * <li> 060 * {@link #getSourceOutputPackages()} - for fetching all generated source packages. 061 * </li> 062 * <li> 063 * {@link #getSourceOutputModules()} - for fetching all generated source modules. 064 * </li> 065 * <li> 066 * {@link #close()} - to close any resources in the temporary file system. 067 * </li> 068 * </ul> 069 * 070 * <p>A simple example of usage of this interface would be the following: 071 * 072 * <pre><code> 073 * try (Workspace workspace = Workspaces.newWorkspace()) { 074 * workspace 075 * .createSourcePathPackage() 076 * .copyContentsFrom("src", "test", "resources", "test-data"); 077 * 078 * var compilation = someCompiler.compile(workspace); 079 * 080 * assertThat(compilation).isSuccessful(); 081 * } 082 * </code></pre> 083 * 084 * <p>As of 3.2.0, you can use a functional version of the above instead if this is more 085 * suitable for your use-case: 086 * 087 * <pre><code> 088 * Workspaces.newWorkspace().use(workspace -> { 089 * ... 090 * }); 091 * </code></pre> 092 * 093 * <p>Remember that files that are created as the result of a compilation can be queried via 094 * {@link JctFileManager}, which is accessible on the {@code compilation} result object. This may 095 * more accurately represent the logical project structure that is the result of various 096 * processing operations during compilation. 097 * 098 * @author Ashley Scopes 099 * @since 0.0.1 100 */ 101@SuppressWarnings({"unused", "UnusedReturnValue"}) 102public interface Workspace extends AutoCloseable { 103 104 /** 105 * Attempt to close all resources in this workspace. 106 * 107 * @throws UncheckedIOException if an error occurs. 108 */ 109 @Override 110 void close(); 111 112 /** 113 * Dump a visual representation of this workspace to the given appendable. 114 * 115 * <p>This provides a utility method for debugging. 116 * 117 * @param appendable the appendable to write to. 118 * @throws UncheckedIOException if an IO error occurs when writing to the appendable. 119 * @since 5.0.0 120 */ 121 void dump(Appendable appendable); 122 123 /** 124 * Determine if the workspace is closed or not. 125 * 126 * @return {@code true} if closed, {@code false} if open. 127 * @since 0.4.0 128 */ 129 boolean isClosed(); 130 131 /** 132 * Get an immutable copy of the current paths to operate on. 133 * 134 * @return the paths. 135 */ 136 Map<Location, List<? extends PathRoot>> getAllPaths(); 137 138 /** 139 * Get the collection of path roots associated with the given module. 140 * 141 * <p>Usually this should only ever contain one path root at a maximum, although 142 * {@link Workspace} does not explicitly enforce this constraint. 143 * 144 * <p>If no results were found, then an empty collection is returned. 145 * 146 * @param location the module-oriented or output location. 147 * @param moduleName the module name within the location. 148 * @return the collection of paths. 149 * @throws IllegalArgumentException if the location is neither 150 * {@link Location#isModuleOrientedLocation() module-oriented} or 151 * an {@link Location#isOutputLocation() output location}. This 152 * will also be raised if this method is called with an instance 153 * of {@link ModuleLocation} (you should use 154 * {@link #getPackages(Location)} instead for this). 155 * @see #getPackages(Location) 156 * @see #getModules(Location) 157 * @since 0.1.0 158 */ 159 List<? extends PathRoot> getModule(Location location, String moduleName); 160 161 /** 162 * Get the collection of modules associated with the given location. 163 * 164 * <p>If no results were found, then an empty map is returned. 165 * 166 * @param location the location to get the modules for. 167 * @return the map of module names to lists of associated paths. 168 * @throws IllegalArgumentException if the location is neither 169 * {@link Location#isModuleOrientedLocation() module-oriented} or 170 * an {@link Location#isOutputLocation() output location}. This 171 * will also be raised if this method is called with an instance 172 * of {@link ModuleLocation}. 173 * @see #getModule(Location, String) 174 * @since 0.1.0 175 */ 176 Map<String, List<? extends PathRoot>> getModules(Location location); 177 178 /** 179 * Get the path strategy in use. 180 * 181 * @return the path strategy. 182 */ 183 PathStrategy getPathStrategy(); 184 185 /** 186 * Get the collection of path roots associated with the given location. 187 * 188 * <p>If no results were found, then an empty collection is returned. 189 * 190 * @param location the location to get. 191 * @return the collection of paths. 192 * @throws IllegalArgumentException if the location is 193 * {@link Location#isModuleOrientedLocation() module-oriented}. 194 * @see #getModule(Location, String) 195 * @since 0.1.0 196 */ 197 List<? extends PathRoot> getPackages(Location location); 198 199 /** 200 * Add an existing package root to this workspace and associate it with the given location. 201 * 202 * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException} 203 * will be thrown: 204 * 205 * <ul> 206 * <li> 207 * The {@code location} must not be 208 * {@link Location#isModuleOrientedLocation() module-oriented}. 209 * </li> 210 * <li> 211 * The {@code path} must exist. 212 * </li> 213 * </ul> 214 * 215 * <p>For example, this would add the package tree in {@code src/test/resources/packages} 216 * into the {@link StandardLocation#SOURCE_PATH source code path} that is used to compile files: 217 * 218 * <pre><code> 219 * var path = Path.of("src", "test", "resources", "packages"); 220 * workspace.addPackage(StandardLocation.SOURCE_PATH, path); 221 * </code></pre> 222 * 223 * @param location the location to associate with. 224 * @param path the path to add. 225 * @throws IllegalArgumentException if the inputs are invalid. 226 * @see #addModule(Location, String, Path) 227 * @see #createPackage(Location) 228 * @see #createModule(Location, String) 229 */ 230 void addPackage(Location location, Path path); 231 232 /** 233 * Add an existing package root to this workspace and associate it with the given module name in 234 * the given location. 235 * 236 * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException} 237 * will be thrown: 238 * 239 * <ul> 240 * <li> 241 * The {@code location} must be {@link Location#isModuleOrientedLocation() module-oriented} 242 * or an {@link Location#isOutputLocation() output location}. 243 * </li> 244 * <li> 245 * The {@code location} must not be a {@link ModuleLocation module-location handle} already. 246 * </li> 247 * <li> 248 * The {@code path} must exist. 249 * </li> 250 * </ul> 251 * 252 * <p>For example, this would add the package tree in {@code src/test/resources/packages} 253 * into the {@link StandardLocation#MODULE_SOURCE_PATH module source code path} under the module 254 * name {@code foo.bar}: 255 * 256 * <pre><code> 257 * var path = Path.of("src", "test", "resources", "packages"); 258 * workspace.addModule(StandardLocation.MODULE_SOURCE_PATH, "foo.bar", path); 259 * </code></pre> 260 * 261 * @param location the location to associate with. 262 * @param moduleName the name of the module. 263 * @param path the path to add. 264 * @throws IllegalArgumentException if the inputs are invalid. 265 * @see #addPackage(Location, Path) 266 * @see #createPackage(Location) 267 * @see #createModule(Location, String) 268 */ 269 void addModule(Location location, String moduleName, Path path); 270 271 /** 272 * Create a new test directory for a package root and associate it with the given location. 273 * 274 * <p>This path will be destroyed when the workspace is {@link #close() closed}. 275 * 276 * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException} 277 * will be thrown: 278 * 279 * <ul> 280 * <li> 281 * The {@code location} must not be 282 * {@link Location#isModuleOrientedLocation() module-oriented}. 283 * </li> 284 * </ul> 285 * 286 * <p>For example, this would create a new source root that you could add files to: 287 * 288 * <pre><code> 289 * workspace.createPackage(StandardLocation.SOURCE_PATH); 290 * </code></pre> 291 * 292 * @param location the location to associate with. 293 * @return the test directory that was created. 294 * @throws IllegalArgumentException if the inputs are invalid. 295 * @see #createModule(Location, String) 296 * @see #addPackage(Location, Path) 297 * @see #addModule(Location, String, Path) 298 */ 299 ManagedDirectory createPackage(Location location); 300 301 /** 302 * Create a new test directory for a package root and associate it with the given module name in 303 * the given location. 304 * 305 * <p>This path will be destroyed when the workspace is {@link #close() closed}. 306 * 307 * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException} 308 * will be thrown: 309 * 310 * <ul> 311 * <li> 312 * The {@code location} must be {@link Location#isModuleOrientedLocation() module-oriented} 313 * or an {@link Location#isOutputLocation() output location}. 314 * </li> 315 * <li> 316 * The {@code location} must not be a {@link ModuleLocation module-location handle} already. 317 * </li> 318 * </ul> 319 * 320 * <p>For example, this would create a new multi-module source root that you could add files to, 321 * for a module named {@code foo.bar}: 322 * 323 * <pre><code> 324 * workspace.createModule(StandardLocation.MODULE_SOURCE_PATH, "foo.bar"); 325 * </code></pre> 326 * 327 * @param location the location to associate with. 328 * @param moduleName the module name to use. 329 * @return the test directory that was created. 330 * @throws IllegalArgumentException if the inputs are invalid. 331 * @see #createPackage(Location) 332 * @see #addPackage(Location, Path) 333 * @see #addModule(Location, String, Path) 334 */ 335 ManagedDirectory createModule(Location location, String moduleName); 336 337 /** 338 * Add a package to the {@link StandardLocation#CLASS_OUTPUT class outputs}. 339 * 340 * @param path the path to add. 341 * @throws IllegalArgumentException if the path does not exist. 342 * @see #addPackage(Location, Path) 343 * @see #addClassOutputModule(String, Path) 344 * @see #createClassOutputPackage() 345 * @see #createClassOutputModule(String) 346 */ 347 default void addClassOutputPackage(Path path) { 348 addPackage(StandardLocation.CLASS_OUTPUT, path); 349 } 350 351 /** 352 * Add a module to the {@link StandardLocation#CLASS_OUTPUT class outputs}. 353 * 354 * @param moduleName the module name. 355 * @param path the path to add. 356 * @throws IllegalArgumentException if the path does not exist. 357 * @see #addModule(Location, String, Path) 358 * @see #addClassOutputPackage(Path) 359 * @see #createClassOutputPackage() 360 * @see #createClassOutputModule(String) 361 */ 362 default void addClassOutputModule(String moduleName, Path path) { 363 addModule(StandardLocation.CLASS_OUTPUT, moduleName, path); 364 } 365 366 /** 367 * Add a package to the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}. 368 * 369 * @param path the path to add. 370 * @throws IllegalArgumentException if the path does not exist. 371 * @see #addPackage(Location, Path) 372 * @see #addSourceOutputModule(String, Path) 373 * @see #createSourceOutputPackage() 374 * @see #createSourceOutputModule(String) 375 */ 376 default void addSourceOutputPackage(Path path) { 377 addPackage(StandardLocation.SOURCE_OUTPUT, path); 378 } 379 380 /** 381 * Add a module to the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}. 382 * 383 * @param moduleName the module name. 384 * @param path the path to add. 385 * @throws IllegalArgumentException if the path does not exist. 386 * @see #addModule(Location, String, Path) 387 * @see #addSourceOutputPackage(Path) 388 * @see #createSourceOutputPackage() 389 * @see #createSourceOutputModule(String) 390 */ 391 default void addSourceOutputModule(String moduleName, Path path) { 392 addModule(StandardLocation.SOURCE_OUTPUT, moduleName, path); 393 } 394 395 /** 396 * Add a package to the {@link StandardLocation#CLASS_PATH class path}. 397 * 398 * <p>If you are adding JPMS modules, you may want to use 399 * {@link #addModulePathModule(String, Path)} instead. 400 * 401 * @param path the path to add. 402 * @throws IllegalArgumentException if the path does not exist. 403 * @see #addPackage(Location, Path) 404 * @see #addModulePathModule(String, Path) 405 * @see #createClassPathPackage() 406 * @see #createModulePathModule(String) 407 */ 408 default void addClassPathPackage(Path path) { 409 addPackage(StandardLocation.CLASS_PATH, path); 410 } 411 412 /** 413 * Add a module to the {@link StandardLocation#MODULE_PATH module path}. 414 * 415 * <p>If you are adding non-JPMS modules, you may want to use 416 * {@link #addClassPathPackage(Path)} instead. 417 * 418 * @param moduleName the module name. 419 * @param path the path to add. 420 * @throws IllegalArgumentException if the path does not exist. 421 * @see #addModule(Location, String, Path) 422 * @see #addClassPathPackage(Path) 423 * @see #createClassPathPackage() 424 * @see #createModulePathModule(String) 425 */ 426 default void addModulePathModule(String moduleName, Path path) { 427 addModule(StandardLocation.MODULE_PATH, moduleName, path); 428 } 429 430 /** 431 * Add a package to the {@link StandardLocation#SOURCE_PATH legacy source path}. 432 * 433 * <p>This is the location you will usually tend to use for your source code. 434 * 435 * <p>If you wish to define multiple JPMS modules in your source code tree to compile together, 436 * you will want to consider using {@link #addSourcePathModule(String, Path)} instead. For most 437 * purposes, however, this method is the one you will want to be using if your code is not in a 438 * <strong>named</strong> module directory (so not something like 439 * {@code src/my.module/org/example/...}). 440 * 441 * @param path the path to add. 442 * @throws IllegalArgumentException if the path does not exist. 443 * @see #addPackage(Location, Path) 444 * @see #addSourcePathModule(String, Path) 445 * @see #createSourcePathPackage() 446 * @see #createSourcePathModule(String) 447 */ 448 default void addSourcePathPackage(Path path) { 449 addPackage(StandardLocation.SOURCE_PATH, path); 450 } 451 452 /** 453 * Add a module to the {@link StandardLocation#MODULE_SOURCE_PATH module source path}. 454 * 455 * <p>Note that this will signal to the compiler to run in multi-module compilation mode. 456 * Any source packages that were {@link #addSourcePathPackage(Path) added} or 457 * {@link #createSourcePathPackage() created} will be ignored. 458 * 459 * <p>If you are using a single-module, you can 460 * {@link #addSourcePathPackage(Path) add a source package} and include a {@code module-info.java} 461 * in the base directory instead. 462 * 463 * @param moduleName the module name. 464 * @param path the path to add. 465 * @throws IllegalArgumentException if the path does not exist. 466 * @see #addModule(Location, String, Path) 467 * @see #addSourcePathPackage(Path) 468 * @see #createSourcePathPackage() 469 * @see #createSourcePathModule(String) 470 */ 471 default void addSourcePathModule(String moduleName, Path path) { 472 addModule(StandardLocation.MODULE_SOURCE_PATH, moduleName, path); 473 } 474 475 /** 476 * Add a package to the 477 * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}. 478 * 479 * <p>Note that this will be ignored if the compiler is provided with explicit annotation 480 * processor instances to run. 481 * 482 * @param path the path to add. 483 * @throws IllegalArgumentException if the path does not exist. 484 * @see #addPackage(Location, Path) 485 * @see #addAnnotationProcessorPathModule(String, Path) 486 * @see #createAnnotationProcessorPathPackage() 487 * @see #createAnnotationProcessorPathModule(String) 488 */ 489 default void addAnnotationProcessorPathPackage(Path path) { 490 addPackage(StandardLocation.ANNOTATION_PROCESSOR_PATH, path); 491 } 492 493 /** 494 * Add a module to the 495 * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}. 496 * 497 * <p>Note that this will be ignored if the compiler is provided with explicit annotation 498 * processor instances to run. 499 * 500 * @param moduleName the module name. 501 * @param path the path to add. 502 * @throws IllegalArgumentException if the path does not exist. 503 * @see #addModule(Location, String, Path) 504 * @see #addAnnotationProcessorPathPackage(Path) 505 * @see #createAnnotationProcessorPathPackage() 506 * @see #createAnnotationProcessorPathModule 507 */ 508 default void addAnnotationProcessorPathModule(String moduleName, Path path) { 509 addModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName, path); 510 } 511 512 /** 513 * Create a package in the {@link StandardLocation#CLASS_OUTPUT class outputs}. 514 * 515 * @return the created test directory. 516 * @see #createPackage(Location) 517 * @see #createClassOutputModule(String) 518 * @see #addClassOutputPackage(Path) 519 * @see #addClassOutputModule(String, Path) 520 */ 521 default ManagedDirectory createClassOutputPackage() { 522 return createPackage(StandardLocation.CLASS_OUTPUT); 523 } 524 525 /** 526 * Create a module in the {@link StandardLocation#CLASS_OUTPUT class outputs}. 527 * 528 * @param moduleName the module name. 529 * @return the created test directory. 530 * @see #createModule(Location, String) 531 * @see #createClassOutputPackage() 532 * @see #addClassOutputPackage(Path) 533 * @see #addClassOutputModule(String, Path) 534 */ 535 default ManagedDirectory createClassOutputModule(String moduleName) { 536 return createModule(StandardLocation.CLASS_OUTPUT, moduleName); 537 } 538 539 /** 540 * Create a package in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}. 541 * 542 * @return the created test directory. 543 * @see #createPackage(Location) 544 * @see #createSourceOutputModule(String) 545 * @see #addSourceOutputPackage(Path) 546 * @see #addSourceOutputModule(String, Path) 547 */ 548 default ManagedDirectory createSourceOutputPackage() { 549 return createPackage(StandardLocation.SOURCE_OUTPUT); 550 } 551 552 /** 553 * Create a module in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}. 554 * 555 * @param moduleName the module name. 556 * @return the created test directory. 557 * @see #createModule(Location, String) 558 * @see #createSourceOutputPackage() 559 * @see #addSourceOutputPackage(Path) 560 * @see #addSourceOutputModule(String, Path) 561 */ 562 default ManagedDirectory createSourceOutputModule(String moduleName) { 563 return createModule(StandardLocation.SOURCE_OUTPUT, moduleName); 564 } 565 566 /** 567 * Create a package in the {@link StandardLocation#CLASS_PATH class path}. 568 * 569 * <p>If you are adding JPMS modules, you may want to use 570 * {@link #createModulePathModule(String)} instead. 571 * 572 * @return the created test directory. 573 * @see #createPackage(Location) 574 * @see #createModulePathModule(String) 575 * @see #addClassPathPackage(Path) 576 * @see #addModulePathModule(String, Path) 577 */ 578 default ManagedDirectory createClassPathPackage() { 579 return createPackage(StandardLocation.CLASS_PATH); 580 } 581 582 /** 583 * Create a module in the {@link StandardLocation#MODULE_PATH module path}. 584 * 585 * <p>If you are adding non-JPMS modules, you may want to use 586 * {@link #createClassPathPackage()} instead. 587 * 588 * @param moduleName the module name. 589 * @return the created test directory. 590 * @see #createModule(Location, String) 591 * @see #createClassPathPackage() 592 * @see #addModulePathModule(String, Path) 593 * @see #addClassPathPackage(Path) 594 */ 595 default ManagedDirectory createModulePathModule(String moduleName) { 596 return createModule(StandardLocation.MODULE_PATH, moduleName); 597 } 598 599 /** 600 * Create a package in the {@link StandardLocation#SOURCE_PATH source path}. 601 * 602 * <p>If you wish to define multiple JPMS modules in your source code tree to compile together, 603 * you will want to consider using {@link #createSourcePathModule(String)} instead. For most 604 * purposes, however, this method is the one you will want to be using if your code is not in a 605 * <strong>named</strong> module directory (so not something like 606 * {@code src/my.module/org/example/...}). 607 * 608 * @return the created test directory. 609 * @see #createPackage(Location) 610 * @see #createSourcePathModule(String) 611 * @see #addSourcePathPackage(Path) 612 * @see #addSourcePathModule(String, Path) 613 */ 614 default ManagedDirectory createSourcePathPackage() { 615 return createPackage(StandardLocation.SOURCE_PATH); 616 } 617 618 /** 619 * Create a module in the {@link StandardLocation#MODULE_SOURCE_PATH module source path}. 620 * 621 * <p>Note that this will signal to the compiler to run in multi-module compilation mode. 622 * Any source packages that were {@link #createSourcePathPackage() created} or 623 * {@link #addSourcePathPackage(Path) added} will be ignored. 624 * 625 * <p>If you are using a single-module, you can 626 * {@link #createSourcePathPackage() create a source package} and include a 627 * {@code module-info.java} in the base directory instead. 628 * 629 * @param moduleName the module name. 630 * @return the created test directory. 631 * @see #createModule(Location, String) 632 * @see #createSourcePathPackage() 633 * @see #addSourcePathPackage(Path) 634 * @see #addSourcePathModule(String, Path) 635 */ 636 default ManagedDirectory createSourcePathModule(String moduleName) { 637 return createModule(StandardLocation.MODULE_SOURCE_PATH, moduleName); 638 } 639 640 /** 641 * Create a package in the 642 * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}. 643 * 644 * <p>Note that this will be ignored if the compiler is provided with explicit annotation 645 * processor instances to run. 646 * 647 * @return the created test directory. 648 * @see #createPackage(Location) 649 * @see #createAnnotationProcessorPathModule(String) 650 * @see #addAnnotationProcessorPathPackage(Path) 651 * @see #addAnnotationProcessorPathModule(String, Path) 652 */ 653 default ManagedDirectory createAnnotationProcessorPathPackage() { 654 return createPackage(StandardLocation.ANNOTATION_PROCESSOR_PATH); 655 } 656 657 /** 658 * Create a module in the 659 * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}. 660 * 661 * <p>Note that this will be ignored if the compiler is provided with explicit annotation 662 * processor instances to run. 663 * 664 * @param moduleName the module name. 665 * @return the created test directory. 666 * @see #createModule(Location, String) 667 * @see #createAnnotationProcessorPathPackage() 668 * @see #addAnnotationProcessorPathPackage(Path) 669 * @see #addAnnotationProcessorPathModule(String, Path) 670 */ 671 default ManagedDirectory createAnnotationProcessorPathModule(String moduleName) { 672 return createModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName); 673 } 674 675 /** 676 * Get the non-module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}. 677 * 678 * @return the roots in a collection, or an empty collection if none were found. 679 * @since 0.1.0 680 */ 681 default List<? extends PathRoot> getClassOutputPackages() { 682 return getPackages(StandardLocation.CLASS_OUTPUT); 683 } 684 685 /** 686 * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs} for the given 687 * module name. 688 * 689 * @param moduleName the module name. 690 * @return the roots in a collection, or an empty collection if none were found. 691 * @since 0.1.0 692 */ 693 default List<? extends PathRoot> getClassOutputModule(String moduleName) { 694 return getModule(StandardLocation.CLASS_OUTPUT, moduleName); 695 } 696 697 /** 698 * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}. 699 * 700 * @return the roots in a map of module names to lists of roots, or an empty map if none were 701 * found. 702 * @since 0.1.0 703 */ 704 default Map<String, List<? extends PathRoot>> getClassOutputModules() { 705 return getModules(StandardLocation.CLASS_OUTPUT); 706 } 707 708 /** 709 * Get the non-module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}. 710 * 711 * @return the roots in a collection, or an empty collection if none were found. 712 * @since 0.1.0 713 */ 714 default List<? extends PathRoot> getSourceOutputPackages() { 715 return getPackages(StandardLocation.SOURCE_OUTPUT); 716 } 717 718 /** 719 * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs} for the 720 * given module name. 721 * 722 * @param moduleName the module name. 723 * @return the roots in a collection, or an empty collection if none were found. 724 * @since 0.1.0 725 */ 726 default List<? extends PathRoot> getSourceOutputModule(String moduleName) { 727 return getModule(StandardLocation.SOURCE_OUTPUT, moduleName); 728 } 729 730 731 /** 732 * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}. 733 * 734 * @return the roots in a map of module names to lists of roots, or an empty map if none were 735 * found. 736 * @since 0.1.0 737 */ 738 default Map<String, List<? extends PathRoot>> getSourceOutputModules() { 739 return getModules(StandardLocation.SOURCE_OUTPUT); 740 } 741 742 /** 743 * Get the path roots for the {@link StandardLocation#CLASS_PATH class path}. 744 * 745 * @return the roots in a collection, or an empty collection if none were found. 746 * @since 0.1.0 747 */ 748 default List<? extends PathRoot> getClassPathPackages() { 749 return getPackages(StandardLocation.CLASS_PATH); 750 } 751 752 /** 753 * Get the path roots for the {@link StandardLocation#SOURCE_PATH source path}. 754 * 755 * @return the roots in a collection, or an empty collection if none were found. 756 * @since 0.1.0 757 */ 758 default List<? extends PathRoot> getSourcePathPackages() { 759 return getPackages(StandardLocation.SOURCE_PATH); 760 } 761 762 /** 763 * Get the path roots for the 764 * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}. 765 * 766 * @return the roots in a collection, or an empty collection if none were found. 767 * @since 0.1.0 768 */ 769 default List<? extends PathRoot> getAnnotationProcessorPathPackages() { 770 return getPackages(StandardLocation.ANNOTATION_PROCESSOR_PATH); 771 } 772 773 /** 774 * Get the path roots for the 775 * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}. 776 * 777 * @param moduleName the module name to get. 778 * @return the roots in a collection, or an empty collection if none were found. 779 * @since 0.1.0 780 */ 781 default List<? extends PathRoot> getAnnotationProcessorPathModule(String moduleName) { 782 return getModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName); 783 } 784 785 /** 786 * Get the module path roots for the 787 * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}. 788 * 789 * @return the roots in a map of module names to lists of roots, or an empty map if none were 790 * found. 791 * @since 0.1.0 792 */ 793 default Map<String, List<? extends PathRoot>> getAnnotationProcessorPathModules() { 794 return getModules(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH); 795 } 796 797 /** 798 * Get the path roots for the {@link StandardLocation#MODULE_SOURCE_PATH module source path}. 799 * 800 * @param moduleName the module name to get. 801 * @return the roots in a collection, or an empty collection if none were found. 802 * @since 0.1.0 803 */ 804 default List<? extends PathRoot> getSourcePathModule(String moduleName) { 805 return getModule(StandardLocation.MODULE_SOURCE_PATH, moduleName); 806 } 807 808 /** 809 * Get the module path roots for the 810 * {@link StandardLocation#MODULE_SOURCE_PATH module source paths}. 811 * 812 * @return the roots in a map of module names to lists of roots, or an empty map if none were 813 * found. 814 * @since 0.1.0 815 */ 816 default Map<String, List<? extends PathRoot>> getSourcePathModules() { 817 return getModules(StandardLocation.MODULE_SOURCE_PATH); 818 } 819 820 /** 821 * Get the path roots for the {@link StandardLocation#MODULE_PATH module path}. 822 * 823 * @param moduleName the module name to get. 824 * @return the roots in a collection, or an empty collection if none were found. 825 * @since 0.1.0 826 */ 827 default List<? extends PathRoot> getModulePathModule(String moduleName) { 828 return getModule(StandardLocation.MODULE_PATH, moduleName); 829 } 830 831 /** 832 * Get the module path roots for the {@link StandardLocation#MODULE_PATH module paths}. 833 * 834 * @return the roots in a map of module names to lists of roots, or an empty map if none were 835 * found. 836 * @since 0.1.0 837 */ 838 default Map<String, List<? extends PathRoot>> getModulePathModules() { 839 return getModules(StandardLocation.MODULE_PATH); 840 } 841 842 /** 843 * Functional equivalent of consuming this object with a try-with-resources. 844 * 845 * <p>This workspace will be {@link #close() closed} upon completion of this method or upon 846 * an exception being raised. 847 * 848 * @param consumer the consumer to pass this object to. 849 * @param <T> the exception that the consumer can throw, or {@link RuntimeException} 850 * if no checked exception is thrown. 851 * @throws UncheckedIOException if the closure fails after the consumer is called. 852 * @throws T the checked exception that the consumer can throw. 853 * @since 3.2.0 854 */ 855 default <T extends Throwable> void use(ThrowingWorkspaceConsumer<T> consumer) throws T { 856 try { 857 consumer.accept(this); 858 } finally { 859 close(); 860 } 861 } 862 863 /** 864 * A consumer functional interface that consumes a workspace and 865 * can throw a checked exception. 866 * 867 * @param <T> the exception type that can be thrown, or {@link RuntimeException} 868 * if no checked exception is thrown. 869 * @author Ashley Scopes 870 * @since 3.2.0 871 */ 872 interface ThrowingWorkspaceConsumer<T extends Throwable> { 873 874 /** 875 * Consume a workspace. 876 * 877 * @param workspace the workspace. 878 * @throws T the checked exception that can be thrown. 879 */ 880 void accept(Workspace workspace) throws T; 881 } 882} 883