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