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