001/*
002 * Copyright (C) 2022 - 2023, the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.github.ascopes.jct.workspaces;
017
018import 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(PathStrategy.TEMP_DIRECTORIES)) {
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>Remember that files that are created as the result of a compilation can be queried via
087 * {@link JctFileManager}, which is accessible on the {@code compilation} result object. This may
088 * more accurately represent the logical project structure that is the result of various
089 * processing operations during compilation.
090 *
091 * @author Ashley Scopes
092 * @since 0.0.1
093 */
094@API(since = "0.0.1", status = Status.STABLE)
095@SuppressWarnings({"unused", "UnusedReturnValue"})
096public interface Workspace extends AutoCloseable {
097
098  /**
099   * Attempt to close all resources in this workspace.
100   *
101   * @throws UncheckedIOException if an error occurs.
102   */
103  @Override
104  void close();
105
106  /**
107   * Determine if the workspace is closed or not.
108   *
109   * @return {@code true} if closed, {@code false} if open.
110   * @since 0.4.0
111   */
112  @API(since = "0.4.0", status = Status.STABLE)
113  boolean isClosed();
114
115  ///
116  /// Accessor operations
117  ///
118
119  /**
120   * Get an immutable copy of the current paths to operate on.
121   *
122   * @return the paths.
123   */
124  Map<Location, List<? extends PathRoot>> getAllPaths();
125
126  /**
127   * Get the collection of path roots associated with the given module.
128   *
129   * <p>Usually this should only ever contain one path root at a maximum, although
130   * {@link Workspace} does not explicitly enforce this constraint.
131   *
132   * <p>If no results were found, then an empty collection is returned.
133   *
134   * @param location   the module-oriented or output location.
135   * @param moduleName the module name within the location.
136   * @return the collection of paths.
137   * @throws IllegalArgumentException if the location is neither
138   *                                  {@link Location#isModuleOrientedLocation() module-oriented} or
139   *                                  an {@link Location#isOutputLocation() output location}. This
140   *                                  will also be raised if this method is called with an instance
141   *                                  of {@link ModuleLocation} (you should use
142   *                                  {@link #getPackages(Location)} instead for this).
143   * @see #getPackages(Location)
144   * @see #getModules(Location)
145   * @since 0.1.0
146   */
147  List<? extends PathRoot> getModule(Location location, String moduleName);
148
149  /**
150   * Get the collection of modules associated with the given location.
151   *
152   * <p>If no results were found, then an empty map is returned.
153   *
154   * @param location the location to get the modules for.
155   * @return the map of module names to lists of associated paths.
156   * @throws IllegalArgumentException if the location is neither
157   *                                  {@link Location#isModuleOrientedLocation() module-oriented} or
158   *                                  an {@link Location#isOutputLocation() output location}. This
159   *                                  will also be raised if this method is called with an instance
160   *                                  of {@link ModuleLocation}.
161   * @see #getModule(Location, String)
162   * @since 0.1.0
163   */
164  Map<String, List<? extends PathRoot>> getModules(Location location);
165
166  /**
167   * Get the path strategy in use.
168   *
169   * @return the path strategy.
170   */
171  PathStrategy getPathStrategy();
172
173  /**
174   * Get the collection of path roots associated with the given location.
175   *
176   * <p>If no results were found, then an empty collection is returned.
177   *
178   * @param location the location to get.
179   * @return the collection of paths.
180   * @throws IllegalArgumentException if the location is
181   *                                  {@link Location#isModuleOrientedLocation() module-oriented}.
182   * @see #getModule(Location, String)
183   * @since 0.1.0
184   */
185  List<? extends PathRoot> getPackages(Location location);
186
187  ///
188  /// Mutative operations
189  ///
190
191  /**
192   * Add an existing package root to this workspace and associate it with the given location.
193   *
194   * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException}
195   * will be thrown:
196   *
197   * <ul>
198   *   <li>
199   *     The {@code location} must not be
200   *     {@link Location#isModuleOrientedLocation() module-oriented}.
201   *   </li>
202   *   <li>
203   *     The {@code path} must exist.
204   *   </li>
205   * </ul>
206   *
207   * <p>For example, this would add the package tree in {@code src/test/resources/packages}
208   * into the {@link StandardLocation#SOURCE_PATH source code path} that is used to compile files:
209   *
210   * <pre><code>
211   *   var path = Path.of("src", "test", "resources", "packages");
212   *   workspace.addPackage(StandardLocation.SOURCE_PATH, path);
213   * </code></pre>
214   *
215   * @param location the location to associate with.
216   * @param path     the path to add.
217   * @throws IllegalArgumentException if the inputs are invalid.
218   * @see #addModule(Location, String, Path)
219   * @see #createPackage(Location)
220   * @see #createModule(Location, String)
221   */
222  void addPackage(Location location, Path path);
223
224  /**
225   * Add an existing package root to this workspace and associate it with the given module name in
226   * the given location.
227   *
228   * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException}
229   * will be thrown:
230   *
231   * <ul>
232   *   <li>
233   *     The {@code location} must be {@link Location#isModuleOrientedLocation() module-oriented}
234   *     or an {@link Location#isOutputLocation() output location}.
235   *   </li>
236   *   <li>
237   *     The {@code location} must not be a {@link ModuleLocation module-location handle} already.
238   *   </li>
239   *   <li>
240   *     The {@code path} must exist.
241   *   </li>
242   * </ul>
243   *
244   * <p>For example, this would add the package tree in {@code src/test/resources/packages}
245   * into the {@link StandardLocation#MODULE_SOURCE_PATH module source code path} under the module
246   * name {@code foo.bar}:
247   *
248   * <pre><code>
249   *   var path = Path.of("src", "test", "resources", "packages");
250   *   workspace.addModule(StandardLocation.MODULE_SOURCE_PATH, "foo.bar", path);
251   * </code></pre>
252   *
253   * @param location   the location to associate with.
254   * @param moduleName the name of the module.
255   * @param path       the path to add.
256   * @throws IllegalArgumentException if the inputs are invalid.
257   * @see #addPackage(Location, Path)
258   * @see #createPackage(Location)
259   * @see #createModule(Location, String)
260   */
261  void addModule(Location location, String moduleName, Path path);
262
263  /**
264   * Create a new test directory for a package root and associate it with the given location.
265   *
266   * <p>This path will be destroyed when the workspace is {@link #close() closed}.
267   *
268   * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException}
269   * will be thrown:
270   *
271   * <ul>
272   *   <li>
273   *     The {@code location} must not be
274   *     {@link Location#isModuleOrientedLocation() module-oriented}.
275   *   </li>
276   * </ul>
277   *
278   * <p>For example, this would create a new source root that you could add files to:
279   *
280   * <pre><code>
281   *   workspace.createPackage(StandardLocation.SOURCE_PATH);
282   * </code></pre>
283   *
284   * @param location the location to associate with.
285   * @return the test directory that was created.
286   * @throws IllegalArgumentException if the inputs are invalid.
287   * @see #createModule(Location, String)
288   * @see #addPackage(Location, Path)
289   * @see #addModule(Location, String, Path)
290   */
291  ManagedDirectory createPackage(Location location);
292
293  /**
294   * Create a new test directory for a package root and associate it with the given module name in
295   * the given location.
296   *
297   * <p>This path will be destroyed when the workspace is {@link #close() closed}.
298   *
299   * <p>The following constraints must be met, otherwise an {@link IllegalArgumentException}
300   * will be thrown:
301   *
302   * <ul>
303   *   <li>
304   *     The {@code location} must be {@link Location#isModuleOrientedLocation() module-oriented}
305   *     or an {@link Location#isOutputLocation() output location}.
306   *   </li>
307   *   <li>
308   *     The {@code location} must not be a {@link ModuleLocation module-location handle} already.
309   *   </li>
310   * </ul>
311   *
312   * <p>For example, this would create a new multi-module source root that you could add files to,
313   * for a module named {@code foo.bar}:
314   *
315   * <pre><code>
316   *   workspace.createModule(StandardLocation.MODULE_SOURCE_PATH, "foo.bar");
317   * </code></pre>
318   *
319   * @param location   the location to associate with.
320   * @param moduleName the module name to use.
321   * @return the test directory that was created.
322   * @throws IllegalArgumentException if the inputs are invalid.
323   * @see #createPackage(Location)
324   * @see #addPackage(Location, Path)
325   * @see #addModule(Location, String, Path)
326   */
327  ManagedDirectory createModule(Location location, String moduleName);
328
329  ///
330  /// Default implementation helpers.
331  ///
332
333  /**
334   * Add a package to the {@link StandardLocation#CLASS_OUTPUT class outputs}.
335   *
336   * @param path the path to add.
337   * @throws IllegalArgumentException if the path does not exist.
338   * @see #addPackage(Location, Path)
339   * @see #addClassOutputModule(String, Path)
340   * @see #createClassOutputPackage()
341   * @see #createClassOutputModule(String)
342   */
343  default void addClassOutputPackage(Path path) {
344    addPackage(StandardLocation.CLASS_OUTPUT, path);
345  }
346
347  /**
348   * Add a module to the {@link StandardLocation#CLASS_OUTPUT class outputs}.
349   *
350   * @param moduleName the module name.
351   * @param path       the path to add.
352   * @throws IllegalArgumentException if the path does not exist.
353   * @see #addModule(Location, String, Path)
354   * @see #addClassOutputPackage(Path)
355   * @see #createClassOutputPackage()
356   * @see #createClassOutputModule(String)
357   */
358  default void addClassOutputModule(String moduleName, Path path) {
359    addModule(StandardLocation.CLASS_OUTPUT, moduleName, path);
360  }
361
362  /**
363   * Add a package to the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
364   *
365   * @param path the path to add.
366   * @throws IllegalArgumentException if the path does not exist.
367   * @see #addPackage(Location, Path)
368   * @see #addSourceOutputModule(String, Path)
369   * @see #createSourceOutputPackage()
370   * @see #createSourceOutputModule(String)
371   */
372  default void addSourceOutputPackage(Path path) {
373    addPackage(StandardLocation.SOURCE_OUTPUT, path);
374  }
375
376  /**
377   * Add a module to the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
378   *
379   * @param moduleName the module name.
380   * @param path       the path to add.
381   * @throws IllegalArgumentException if the path does not exist.
382   * @see #addModule(Location, String, Path)
383   * @see #addSourceOutputPackage(Path)
384   * @see #createSourceOutputPackage()
385   * @see #createSourceOutputModule(String)
386   */
387  default void addSourceOutputModule(String moduleName, Path path) {
388    addModule(StandardLocation.SOURCE_OUTPUT, moduleName, path);
389  }
390
391  /**
392   * Add a package to the {@link StandardLocation#CLASS_PATH class path}.
393   *
394   * <p>If you are adding JPMS modules, you may want to use
395   * {@link #addModulePathModule(String, Path)} instead.
396   *
397   * @param path the path to add.
398   * @throws IllegalArgumentException if the path does not exist.
399   * @see #addPackage(Location, Path)
400   * @see #addModulePathModule(String, Path)
401   * @see #createClassPathPackage()
402   * @see #createModulePathModule(String)
403   */
404  default void addClassPathPackage(Path path) {
405    addPackage(StandardLocation.CLASS_PATH, path);
406  }
407
408  /**
409   * Add a module to the {@link StandardLocation#MODULE_PATH module path}.
410   *
411   * <p>If you are adding non-JPMS modules, you may want to use
412   * {@link #addClassPathPackage(Path)} instead.
413   *
414   * @param moduleName the module name.
415   * @param path       the path to add.
416   * @throws IllegalArgumentException if the path does not exist.
417   * @see #addModule(Location, String, Path)
418   * @see #addClassPathPackage(Path)
419   * @see #createClassPathPackage()
420   * @see #createModulePathModule(String)
421   */
422  default void addModulePathModule(String moduleName, Path path) {
423    addModule(StandardLocation.MODULE_PATH, moduleName, path);
424  }
425
426  /**
427   * Add a package to the {@link StandardLocation#SOURCE_PATH legacy source path}.
428   *
429   * <p>This is the location you will usually tend to use for your source code.
430   *
431   * <p>If you wish to define multiple JPMS modules in your source code tree to compile together,
432   * you will want to consider using {@link #addSourcePathModule(String, Path)} instead. For most
433   * purposes, however, this method is the one you will want to be using if your code is not in a
434   * <strong>named</strong> module directory (so not something like
435   * {@code src/my.module/org/example/...}).
436   *
437   * @param path the path to add.
438   * @throws IllegalArgumentException if the path does not exist.
439   * @see #addPackage(Location, Path)
440   * @see #addSourcePathModule(String, Path)
441   * @see #createSourcePathPackage()
442   * @see #createSourcePathModule(String)
443   */
444  default void addSourcePathPackage(Path path) {
445    addPackage(StandardLocation.SOURCE_PATH, path);
446  }
447
448  /**
449   * Add a module to the {@link StandardLocation#MODULE_SOURCE_PATH module source path}.
450   *
451   * <p>Note that this will signal to the compiler to run in multi-module compilation mode.
452   * Any source packages that were {@link #addSourcePathPackage(Path) added} or
453   * {@link #createSourcePathPackage() created} will be ignored.
454   *
455   * <p>If you are using a single-module, you can
456   * {@link #addSourcePathPackage(Path) add a source package} and include a {@code module-info.java}
457   * in the base directory instead.
458   *
459   * @param moduleName the module name.
460   * @param path       the path to add.
461   * @throws IllegalArgumentException if the path does not exist.
462   * @see #addModule(Location, String, Path)
463   * @see #addSourcePathPackage(Path)
464   * @see #createSourcePathPackage()
465   * @see #createSourcePathModule(String)
466   */
467  default void addSourcePathModule(String moduleName, Path path) {
468    addModule(StandardLocation.MODULE_SOURCE_PATH, moduleName, path);
469  }
470
471  /**
472   * Add a package to the
473   * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}.
474   *
475   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
476   * processor instances to run.
477   *
478   * @param path the path to add.
479   * @throws IllegalArgumentException if the path does not exist.
480   * @see #addPackage(Location, Path)
481   * @see #addAnnotationProcessorPathModule(String, Path)
482   * @see #createAnnotationProcessorPathPackage()
483   * @see #createAnnotationProcessorPathModule(String)
484   */
485  default void addAnnotationProcessorPathPackage(Path path) {
486    addPackage(StandardLocation.ANNOTATION_PROCESSOR_PATH, path);
487  }
488
489  /**
490   * Add a module to the
491   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
492   *
493   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
494   * processor instances to run.
495   *
496   * @param moduleName the module name.
497   * @param path       the path to add.
498   * @throws IllegalArgumentException if the path does not exist.
499   * @see #addModule(Location, String, Path)
500   * @see #addAnnotationProcessorPathPackage(Path)
501   * @see #createAnnotationProcessorPathPackage()
502   * @see #createAnnotationProcessorPathModule
503   */
504  default void addAnnotationProcessorPathModule(String moduleName, Path path) {
505    addModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName, path);
506  }
507
508  /**
509   * Add a package to the {@link StandardLocation#PLATFORM_CLASS_PATH platform class path} (also
510   * known as the boot class path).
511   *
512   * @param path the path to add.
513   * @throws IllegalArgumentException if the path does not exist.
514   * @see #addPackage(Location, Path)
515   * @see #createPlatformClassPathPackage()
516   */
517  default void addPlatformClassPathPackage(Path path) {
518    addPackage(StandardLocation.PLATFORM_CLASS_PATH, path);
519  }
520
521  /**
522   * Add a package to the {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
523   *
524   * @param path the path to add.
525   * @throws IllegalArgumentException if the path does not exist.
526   * @see #addPackage(Location, Path)
527   * @see #addNativeHeaderOutputModule(String, Path)
528   * @see #createNativeHeaderOutputPackage()
529   * @see #createNativeHeaderOutputModule(String)
530   */
531  default void addNativeHeaderOutputPackage(Path path) {
532    addPackage(StandardLocation.NATIVE_HEADER_OUTPUT, path);
533  }
534
535  /**
536   * Add a module to the {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
537   *
538   * @param moduleName the name of the module.
539   * @param path       the path to add.
540   * @throws IllegalArgumentException if the path does not exist.
541   * @see #addPackage(Location, Path)
542   * @see #addNativeHeaderOutputPackage(Path)
543   * @see #createNativeHeaderOutputPackage()
544   * @see #createNativeHeaderOutputModule(String)
545   */
546  default void addNativeHeaderOutputModule(String moduleName, Path path) {
547    addModule(StandardLocation.NATIVE_HEADER_OUTPUT, moduleName, path);
548  }
549
550  /**
551   * Add a module to the {@link StandardLocation#UPGRADE_MODULE_PATH upgrade module path}.
552   *
553   * @param moduleName the module name.
554   * @param path       the path to add.
555   * @throws IllegalArgumentException if the path does not exist.
556   * @see #addModule(Location, String, Path)
557   * @see #createUpgradeModulePathModule(String)
558   */
559  default void addUpgradeModulePathModule(String moduleName, Path path) {
560    addModule(StandardLocation.UPGRADE_MODULE_PATH, moduleName, path);
561  }
562
563  /**
564   * Add a module to the {@link StandardLocation#SYSTEM_MODULES system module path}.
565   *
566   * @param moduleName the module name.
567   * @param path       the path to add.
568   * @throws IllegalArgumentException if the path does not exist.
569   * @see #addModule(Location, String, Path)
570   * @see #createSystemModulePathModule(String)
571   */
572  default void addSystemModulePathModule(String moduleName, Path path) {
573    addModule(StandardLocation.SYSTEM_MODULES, moduleName, path);
574  }
575
576  /**
577   * Add a module to the {@link StandardLocation#PATCH_MODULE_PATH patch module path}.
578   *
579   * @param moduleName the module name.
580   * @param path       the path to add.
581   * @throws IllegalArgumentException if the path does not exist.
582   * @see #addModule(Location, String, Path)
583   * @see #createPatchModulePathModule(String)
584   */
585  default void addPatchModulePathModule(String moduleName, Path path) {
586    addModule(StandardLocation.PATCH_MODULE_PATH, moduleName, path);
587  }
588
589  /**
590   * Create a package in the {@link StandardLocation#CLASS_OUTPUT class outputs}.
591   *
592   * @return the created test directory.
593   * @see #createPackage(Location)
594   * @see #createClassOutputModule(String)
595   * @see #addClassOutputPackage(Path)
596   * @see #addClassOutputModule(String, Path)
597   */
598  default ManagedDirectory createClassOutputPackage() {
599    return createPackage(StandardLocation.CLASS_OUTPUT);
600  }
601
602  /**
603   * Create a module in the {@link StandardLocation#CLASS_OUTPUT class outputs}.
604   *
605   * @param moduleName the module name.
606   * @return the created test directory.
607   * @see #createModule(Location, String)
608   * @see #createClassOutputPackage()
609   * @see #addClassOutputPackage(Path)
610   * @see #addClassOutputModule(String, Path)
611   */
612  default ManagedDirectory createClassOutputModule(String moduleName) {
613    return createModule(StandardLocation.CLASS_OUTPUT, moduleName);
614  }
615
616  /**
617   * Create a package in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
618   *
619   * @return the created test directory.
620   * @see #createPackage(Location)
621   * @see #createSourceOutputModule(String)
622   * @see #addSourceOutputPackage(Path)
623   * @see #addSourceOutputModule(String, Path)
624   */
625  default ManagedDirectory createSourceOutputPackage() {
626    return createPackage(StandardLocation.SOURCE_OUTPUT);
627  }
628
629  /**
630   * Create a module in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
631   *
632   * @param moduleName the module name.
633   * @return the created test directory.
634   * @see #createModule(Location, String)
635   * @see #createSourceOutputPackage()
636   * @see #addSourceOutputPackage(Path)
637   * @see #addSourceOutputModule(String, Path)
638   */
639  default ManagedDirectory createSourceOutputModule(String moduleName) {
640    return createModule(StandardLocation.SOURCE_OUTPUT, moduleName);
641  }
642
643  /**
644   * Create a package in the {@link StandardLocation#CLASS_PATH class path}.
645   *
646   * <p>If you are adding JPMS modules, you may want to use
647   * {@link #createModulePathModule(String)} instead.
648   *
649   * @return the created test directory.
650   * @see #createPackage(Location)
651   * @see #createModulePathModule(String)
652   * @see #addClassPathPackage(Path)
653   * @see #addModulePathModule(String, Path)
654   */
655  default ManagedDirectory createClassPathPackage() {
656    return createPackage(StandardLocation.CLASS_PATH);
657  }
658
659  /**
660   * Create a module in the {@link StandardLocation#MODULE_PATH module path}.
661   *
662   * <p>If you are adding non-JPMS modules, you may want to use
663   * {@link #createClassPathPackage()} instead.
664   *
665   * @param moduleName the module name.
666   * @return the created test directory.
667   * @see #createModule(Location, String)
668   * @see #createClassPathPackage()
669   * @see #addModulePathModule(String, Path)
670   * @see #addClassPathPackage(Path)
671   */
672  default ManagedDirectory createModulePathModule(String moduleName) {
673    return createModule(StandardLocation.MODULE_PATH, moduleName);
674  }
675
676  /**
677   * Create a package in the {@link StandardLocation#SOURCE_PATH source path}.
678   *
679   * <p>If you wish to define multiple JPMS modules in your source code tree to compile together,
680   * you will want to consider using {@link #createSourcePathModule(String)} instead. For most
681   * purposes, however, this method is the one you will want to be using if your code is not in a
682   * <strong>named</strong> module directory (so not something like
683   * {@code src/my.module/org/example/...}).
684   *
685   * @return the created test directory.
686   * @see #createPackage(Location)
687   * @see #createSourcePathModule(String)
688   * @see #addSourcePathPackage(Path)
689   * @see #addSourcePathModule(String, Path)
690   */
691  default ManagedDirectory createSourcePathPackage() {
692    return createPackage(StandardLocation.SOURCE_PATH);
693  }
694
695  /**
696   * Create a module in the {@link StandardLocation#MODULE_SOURCE_PATH module source path}.
697   *
698   * <p>Note that this will signal to the compiler to run in multi-module compilation mode.
699   * Any source packages that were {@link #createSourcePathPackage() created} or
700   * {@link #addSourcePathPackage(Path) added} will be ignored.
701   *
702   * <p>If you are using a single-module, you can
703   * {@link #createSourcePathPackage() create a source package} and include a
704   * {@code module-info.java} in the base directory instead.
705   *
706   * @param moduleName the module name.
707   * @return the created test directory.
708   * @see #createModule(Location, String)
709   * @see #createSourcePathPackage()
710   * @see #addSourcePathPackage(Path)
711   * @see #addSourcePathModule(String, Path)
712   */
713  default ManagedDirectory createSourcePathModule(String moduleName) {
714    return createModule(StandardLocation.MODULE_SOURCE_PATH, moduleName);
715  }
716
717  /**
718   * Create a package in the
719   * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}.
720   *
721   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
722   * processor instances to run.
723   *
724   * @return the created test directory.
725   * @see #createPackage(Location)
726   * @see #createAnnotationProcessorPathModule(String)
727   * @see #addAnnotationProcessorPathPackage(Path)
728   * @see #addAnnotationProcessorPathModule(String, Path)
729   */
730  default ManagedDirectory createAnnotationProcessorPathPackage() {
731    return createPackage(StandardLocation.ANNOTATION_PROCESSOR_PATH);
732  }
733
734  /**
735   * Create a module in the
736   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
737   *
738   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
739   * processor instances to run.
740   *
741   * @param moduleName the module name.
742   * @return the created test directory.
743   * @see #createModule(Location, String)
744   * @see #createAnnotationProcessorPathPackage()
745   * @see #addAnnotationProcessorPathPackage(Path)
746   * @see #addAnnotationProcessorPathModule(String, Path)
747   */
748  default ManagedDirectory createAnnotationProcessorPathModule(String moduleName) {
749    return createModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName);
750  }
751
752  /**
753   * Create a package in the {@link StandardLocation#PLATFORM_CLASS_PATH platform class path} (also
754   * known as the boot class path).
755   *
756   * @return the created test directory.
757   * @see #createPackage(Location)
758   * @see #addPlatformClassPathPackage(Path)
759   */
760  default ManagedDirectory createPlatformClassPathPackage() {
761    return createPackage(StandardLocation.PLATFORM_CLASS_PATH);
762  }
763
764  /**
765   * Create a package in the {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
766   *
767   * @return the created test directory.
768   * @see #createPackage(Location)
769   * @see #createNativeHeaderOutputModule(String)
770   * @see #addNativeHeaderOutputPackage(Path)
771   * @see #addNativeHeaderOutputModule(String, Path)
772   */
773  default ManagedDirectory createNativeHeaderOutputPackage() {
774    return createPackage(StandardLocation.NATIVE_HEADER_OUTPUT);
775  }
776
777  /**
778   * Create a module in the {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
779   *
780   * @param moduleName the module name.
781   * @return the created test directory.
782   * @see #createModule(Location, String)
783   * @see #createNativeHeaderOutputPackage()
784   * @see #addNativeHeaderOutputPackage(Path)
785   * @see #addNativeHeaderOutputModule(String, Path)
786   */
787  default ManagedDirectory createNativeHeaderOutputModule(String moduleName) {
788    return createModule(StandardLocation.NATIVE_HEADER_OUTPUT, moduleName);
789  }
790
791  /**
792   * Create a module in the {@link StandardLocation#UPGRADE_MODULE_PATH upgrade module path}.
793   *
794   * @param moduleName the module name.
795   * @return the created test directory.
796   * @see #createModule(Location, String)
797   * @see #addUpgradeModulePathModule(String, Path)
798   */
799  default ManagedDirectory createUpgradeModulePathModule(String moduleName) {
800    return createModule(StandardLocation.UPGRADE_MODULE_PATH, moduleName);
801  }
802
803  /**
804   * Create a module in the {@link StandardLocation#SYSTEM_MODULES system module path}.
805   *
806   * @param moduleName the module name.
807   * @return the created test directory.
808   * @see #createModule(Location, String)
809   * @see #addSystemModulePathModule(String, Path)
810   */
811  default ManagedDirectory createSystemModulePathModule(String moduleName) {
812    return createModule(StandardLocation.SYSTEM_MODULES, moduleName);
813  }
814
815  /**
816   * Create a module in the {@link StandardLocation#PATCH_MODULE_PATH patch module path}.
817   *
818   * @param moduleName the module name.
819   * @return the created test directory.
820   * @see #createModule(Location, String)
821   * @see #createPatchModulePathModule(String)
822   */
823  default ManagedDirectory createPatchModulePathModule(String moduleName) {
824    return createModule(StandardLocation.PATCH_MODULE_PATH, moduleName);
825  }
826
827  /**
828   * Get the non-module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}.
829   *
830   * @return the roots in a collection, or an empty collection if none were found.
831   * @since 0.1.0
832   */
833  default List<? extends PathRoot> getClassOutputPackages() {
834    return getPackages(StandardLocation.CLASS_OUTPUT);
835  }
836
837  /**
838   * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs} for the given
839   * module name.
840   *
841   * @param moduleName the module name.
842   * @return the roots in a collection, or an empty collection if none were found.
843   * @since 0.1.0
844   */
845  default List<? extends PathRoot> getClassOutputModule(String moduleName) {
846    return getModule(StandardLocation.CLASS_OUTPUT, moduleName);
847  }
848
849  /**
850   * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}.
851   *
852   * @return the roots in a map of module names to lists of roots, or an empty map if none were
853   *     found.
854   * @since 0.1.0
855   */
856  default Map<String, List<? extends PathRoot>> getClassOutputModules() {
857    return getModules(StandardLocation.CLASS_OUTPUT);
858  }
859
860  /**
861   * Get the non-module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}.
862   *
863   * @return the roots in a collection, or an empty collection if none were found.
864   * @since 0.1.0
865   */
866  default List<? extends PathRoot> getSourceOutputPackages() {
867    return getPackages(StandardLocation.SOURCE_OUTPUT);
868  }
869
870  /**
871   * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs} for the
872   * given module name.
873   *
874   * @param moduleName the module name.
875   * @return the roots in a collection, or an empty collection if none were found.
876   * @since 0.1.0
877   */
878  default List<? extends PathRoot> getSourceOutputModule(String moduleName) {
879    return getModule(StandardLocation.SOURCE_OUTPUT, moduleName);
880  }
881
882
883  /**
884   * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}.
885   *
886   * @return the roots in a map of module names to lists of roots, or an empty map if none were
887   *     found.
888   * @since 0.1.0
889   */
890  default Map<String, List<? extends PathRoot>> getSourceOutputModules() {
891    return getModules(StandardLocation.SOURCE_OUTPUT);
892  }
893
894  /**
895   * Get the path roots for the {@link StandardLocation#CLASS_PATH class path}.
896   *
897   * @return the roots in a collection, or an empty collection if none were found.
898   * @since 0.1.0
899   */
900  default List<? extends PathRoot> getClassPathPackages() {
901    return getPackages(StandardLocation.CLASS_PATH);
902  }
903
904  /**
905   * Get the path roots for the {@link StandardLocation#SOURCE_PATH source path}.
906   *
907   * @return the roots in a collection, or an empty collection if none were found.
908   * @since 0.1.0
909   */
910  default List<? extends PathRoot> getSourcePathPackages() {
911    return getPackages(StandardLocation.SOURCE_PATH);
912  }
913
914  /**
915   * Get the path roots for the
916   * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}.
917   *
918   * @return the roots in a collection, or an empty collection if none were found.
919   * @since 0.1.0
920   */
921  default List<? extends PathRoot> getAnnotationProcessorPathPackages() {
922    return getPackages(StandardLocation.ANNOTATION_PROCESSOR_PATH);
923  }
924
925  /**
926   * Get the path roots for the
927   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
928   *
929   * @param moduleName the module name to get.
930   * @return the roots in a collection, or an empty collection if none were found.
931   * @since 0.1.0
932   */
933  default List<? extends PathRoot> getAnnotationProcessorPathModule(String moduleName) {
934    return getModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName);
935  }
936
937  /**
938   * Get the module path roots for the
939   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
940   *
941   * @return the roots in a map of module names to lists of roots, or an empty map if none were
942   *     found.
943   * @since 0.1.0
944   */
945  default Map<String, List<? extends PathRoot>> getAnnotationProcessorPathModules() {
946    return getModules(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH);
947  }
948
949  /**
950   * Get the path roots for the {@link StandardLocation#PLATFORM_CLASS_PATH platform class path}
951   * (also known as the boot class path).
952   *
953   * @return the roots in a collection, or an empty collection if none were found.
954   * @since 0.1.0
955   */
956  default List<? extends PathRoot> getPlatformClassPathPackages() {
957    return getPackages(StandardLocation.PLATFORM_CLASS_PATH);
958  }
959
960  /**
961   * Get the non-module path roots for
962   * {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
963   *
964   * @return the roots in a collection, or an empty collection if none were found.
965   * @since 0.1.0
966   */
967  default List<? extends PathRoot> getNativeHeaderOutputPackages() {
968    return getPackages(StandardLocation.NATIVE_HEADER_OUTPUT);
969  }
970
971  /**
972   * Get the module path roots for
973   * {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs} for the given module name.
974   *
975   * @param moduleName the module name.
976   * @return the roots in a collection, or an empty collection if none were found.
977   * @since 0.1.0
978   */
979  default List<? extends PathRoot> getNativeHeaderOutputModule(String moduleName) {
980    return getModule(StandardLocation.NATIVE_HEADER_OUTPUT, moduleName);
981  }
982
983  /**
984   * Get the module path roots for
985   * {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}.
986   *
987   * @return the roots in a map of module names to lists of roots, or an empty map if none were
988   *     found.
989   * @since 0.1.0
990   */
991  default Map<String, List<? extends PathRoot>> getNativeHeaderOutputModules() {
992    return getModules(StandardLocation.NATIVE_HEADER_OUTPUT);
993  }
994
995  /**
996   * Get the path roots for the {@link StandardLocation#MODULE_SOURCE_PATH module source path}.
997   *
998   * @param moduleName the module name to get.
999   * @return the roots in a collection, or an empty collection if none were found.
1000   * @since 0.1.0
1001   */
1002  default List<? extends PathRoot> getSourcePathModule(String moduleName) {
1003    return getModule(StandardLocation.MODULE_SOURCE_PATH, moduleName);
1004  }
1005
1006  /**
1007   * Get the module path roots for the
1008   * {@link StandardLocation#MODULE_SOURCE_PATH module source paths}.
1009   *
1010   * @return the roots in a map of module names to lists of roots, or an empty map if none were
1011   *     found.
1012   * @since 0.1.0
1013   */
1014  default Map<String, List<? extends PathRoot>> getSourcePathModules() {
1015    return getModules(StandardLocation.MODULE_SOURCE_PATH);
1016  }
1017
1018  /**
1019   * Get the path roots for the {@link StandardLocation#UPGRADE_MODULE_PATH upgrade module path}.
1020   *
1021   * @param moduleName the module name to get.
1022   * @return the roots in a collection, or an empty collection if none were found.
1023   * @since 0.1.0
1024   */
1025  default List<? extends PathRoot> getUpgradeModulePathModule(String moduleName) {
1026    return getModule(StandardLocation.UPGRADE_MODULE_PATH, moduleName);
1027  }
1028
1029  /**
1030   * Get the module path roots for the
1031   * {@link StandardLocation#UPGRADE_MODULE_PATH upgrade module paths}.
1032   *
1033   * @return the roots in a map of module names to lists of roots, or an empty map if none were
1034   *     found.
1035   * @since 0.1.0
1036   */
1037  default Map<String, List<? extends PathRoot>> getUpgradeModulePathModules() {
1038    return getModules(StandardLocation.UPGRADE_MODULE_PATH);
1039  }
1040
1041  /**
1042   * Get the path roots for the {@link StandardLocation#SYSTEM_MODULES system modules}.
1043   *
1044   * @param moduleName the module name to get.
1045   * @return the roots in a collection, or an empty collection if none were found.
1046   * @since 0.1.0
1047   */
1048  default List<? extends PathRoot> getSystemModulePathModule(String moduleName) {
1049    return getModule(StandardLocation.SYSTEM_MODULES, moduleName);
1050  }
1051
1052  /**
1053   * Get the module path roots for the {@link StandardLocation#SYSTEM_MODULES system modules}.
1054   *
1055   * @return the roots in a map of module names to lists of roots, or an empty map if none were
1056   *     found.
1057   * @since 0.1.0
1058   */
1059  default Map<String, List<? extends PathRoot>> getSystemModulePathModules() {
1060    return getModules(StandardLocation.SYSTEM_MODULES);
1061  }
1062
1063  /**
1064   * Get the path roots for the {@link StandardLocation#MODULE_PATH module path}.
1065   *
1066   * @param moduleName the module name to get.
1067   * @return the roots in a collection, or an empty collection if none were found.
1068   * @since 0.1.0
1069   */
1070  default List<? extends PathRoot> getModulePathModule(String moduleName) {
1071    return getModule(StandardLocation.MODULE_PATH, moduleName);
1072  }
1073
1074  /**
1075   * Get the module path roots for the {@link StandardLocation#MODULE_PATH module paths}.
1076   *
1077   * @return the roots in a map of module names to lists of roots, or an empty map if none were
1078   *     found.
1079   * @since 0.1.0
1080   */
1081  default Map<String, List<? extends PathRoot>> getModulePathModules() {
1082    return getModules(StandardLocation.MODULE_PATH);
1083  }
1084
1085  /**
1086   * Get the path roots for the {@link StandardLocation#PATCH_MODULE_PATH patch module path}.
1087   *
1088   * @param moduleName the module name to get.
1089   * @return the roots in a collection, or an empty collection if none were found.
1090   * @since 0.1.0
1091   */
1092  default List<? extends PathRoot> getPatchModulePathModule(String moduleName) {
1093    return getModule(StandardLocation.PATCH_MODULE_PATH, moduleName);
1094  }
1095
1096  /**
1097   * Get the module path roots for the
1098   * {@link StandardLocation#PATCH_MODULE_PATH patch module paths}.
1099   *
1100   * @return the roots in a map of module names to lists of roots, or an empty map if none were
1101   *     found.
1102   * @since 0.1.0
1103   */
1104  default Map<String, List<? extends PathRoot>> getPatchModulePathModules() {
1105    return getModules(StandardLocation.PATCH_MODULE_PATH);
1106  }
1107}