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