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(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   * Create a package in the {@link StandardLocation#CLASS_OUTPUT class outputs}.
510   *
511   * @return the created test directory.
512   * @see #createPackage(Location)
513   * @see #createClassOutputModule(String)
514   * @see #addClassOutputPackage(Path)
515   * @see #addClassOutputModule(String, Path)
516   */
517  default ManagedDirectory createClassOutputPackage() {
518    return createPackage(StandardLocation.CLASS_OUTPUT);
519  }
520
521  /**
522   * Create a module in the {@link StandardLocation#CLASS_OUTPUT class outputs}.
523   *
524   * @param moduleName the module name.
525   * @return the created test directory.
526   * @see #createModule(Location, String)
527   * @see #createClassOutputPackage()
528   * @see #addClassOutputPackage(Path)
529   * @see #addClassOutputModule(String, Path)
530   */
531  default ManagedDirectory createClassOutputModule(String moduleName) {
532    return createModule(StandardLocation.CLASS_OUTPUT, moduleName);
533  }
534
535  /**
536   * Create a package in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
537   *
538   * @return the created test directory.
539   * @see #createPackage(Location)
540   * @see #createSourceOutputModule(String)
541   * @see #addSourceOutputPackage(Path)
542   * @see #addSourceOutputModule(String, Path)
543   */
544  default ManagedDirectory createSourceOutputPackage() {
545    return createPackage(StandardLocation.SOURCE_OUTPUT);
546  }
547
548  /**
549   * Create a module in the {@link StandardLocation#SOURCE_OUTPUT generated source outputs}.
550   *
551   * @param moduleName the module name.
552   * @return the created test directory.
553   * @see #createModule(Location, String)
554   * @see #createSourceOutputPackage()
555   * @see #addSourceOutputPackage(Path)
556   * @see #addSourceOutputModule(String, Path)
557   */
558  default ManagedDirectory createSourceOutputModule(String moduleName) {
559    return createModule(StandardLocation.SOURCE_OUTPUT, moduleName);
560  }
561
562  /**
563   * Create a package in the {@link StandardLocation#CLASS_PATH class path}.
564   *
565   * <p>If you are adding JPMS modules, you may want to use
566   * {@link #createModulePathModule(String)} instead.
567   *
568   * @return the created test directory.
569   * @see #createPackage(Location)
570   * @see #createModulePathModule(String)
571   * @see #addClassPathPackage(Path)
572   * @see #addModulePathModule(String, Path)
573   */
574  default ManagedDirectory createClassPathPackage() {
575    return createPackage(StandardLocation.CLASS_PATH);
576  }
577
578  /**
579   * Create a module in the {@link StandardLocation#MODULE_PATH module path}.
580   *
581   * <p>If you are adding non-JPMS modules, you may want to use
582   * {@link #createClassPathPackage()} instead.
583   *
584   * @param moduleName the module name.
585   * @return the created test directory.
586   * @see #createModule(Location, String)
587   * @see #createClassPathPackage()
588   * @see #addModulePathModule(String, Path)
589   * @see #addClassPathPackage(Path)
590   */
591  default ManagedDirectory createModulePathModule(String moduleName) {
592    return createModule(StandardLocation.MODULE_PATH, moduleName);
593  }
594
595  /**
596   * Create a package in the {@link StandardLocation#SOURCE_PATH source path}.
597   *
598   * <p>If you wish to define multiple JPMS modules in your source code tree to compile together,
599   * you will want to consider using {@link #createSourcePathModule(String)} instead. For most
600   * purposes, however, this method is the one you will want to be using if your code is not in a
601   * <strong>named</strong> module directory (so not something like
602   * {@code src/my.module/org/example/...}).
603   *
604   * @return the created test directory.
605   * @see #createPackage(Location)
606   * @see #createSourcePathModule(String)
607   * @see #addSourcePathPackage(Path)
608   * @see #addSourcePathModule(String, Path)
609   */
610  default ManagedDirectory createSourcePathPackage() {
611    return createPackage(StandardLocation.SOURCE_PATH);
612  }
613
614  /**
615   * Create a module in the {@link StandardLocation#MODULE_SOURCE_PATH module source path}.
616   *
617   * <p>Note that this will signal to the compiler to run in multi-module compilation mode.
618   * Any source packages that were {@link #createSourcePathPackage() created} or
619   * {@link #addSourcePathPackage(Path) added} will be ignored.
620   *
621   * <p>If you are using a single-module, you can
622   * {@link #createSourcePathPackage() create a source package} and include a
623   * {@code module-info.java} in the base directory instead.
624   *
625   * @param moduleName the module name.
626   * @return the created test directory.
627   * @see #createModule(Location, String)
628   * @see #createSourcePathPackage()
629   * @see #addSourcePathPackage(Path)
630   * @see #addSourcePathModule(String, Path)
631   */
632  default ManagedDirectory createSourcePathModule(String moduleName) {
633    return createModule(StandardLocation.MODULE_SOURCE_PATH, moduleName);
634  }
635
636  /**
637   * Create a package in the
638   * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}.
639   *
640   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
641   * processor instances to run.
642   *
643   * @return the created test directory.
644   * @see #createPackage(Location)
645   * @see #createAnnotationProcessorPathModule(String)
646   * @see #addAnnotationProcessorPathPackage(Path)
647   * @see #addAnnotationProcessorPathModule(String, Path)
648   */
649  default ManagedDirectory createAnnotationProcessorPathPackage() {
650    return createPackage(StandardLocation.ANNOTATION_PROCESSOR_PATH);
651  }
652
653  /**
654   * Create a module in the
655   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
656   *
657   * <p>Note that this will be ignored if the compiler is provided with explicit annotation
658   * processor instances to run.
659   *
660   * @param moduleName the module name.
661   * @return the created test directory.
662   * @see #createModule(Location, String)
663   * @see #createAnnotationProcessorPathPackage()
664   * @see #addAnnotationProcessorPathPackage(Path)
665   * @see #addAnnotationProcessorPathModule(String, Path)
666   */
667  default ManagedDirectory createAnnotationProcessorPathModule(String moduleName) {
668    return createModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName);
669  }
670
671  /**
672   * Get the non-module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}.
673   *
674   * @return the roots in a collection, or an empty collection if none were found.
675   * @since 0.1.0
676   */
677  default List<? extends PathRoot> getClassOutputPackages() {
678    return getPackages(StandardLocation.CLASS_OUTPUT);
679  }
680
681  /**
682   * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs} for the given
683   * module name.
684   *
685   * @param moduleName the module name.
686   * @return the roots in a collection, or an empty collection if none were found.
687   * @since 0.1.0
688   */
689  default List<? extends PathRoot> getClassOutputModule(String moduleName) {
690    return getModule(StandardLocation.CLASS_OUTPUT, moduleName);
691  }
692
693  /**
694   * Get the module path roots for {@link StandardLocation#CLASS_OUTPUT class outputs}.
695   *
696   * @return the roots in a map of module names to lists of roots, or an empty map if none were
697   *     found.
698   * @since 0.1.0
699   */
700  default Map<String, List<? extends PathRoot>> getClassOutputModules() {
701    return getModules(StandardLocation.CLASS_OUTPUT);
702  }
703
704  /**
705   * Get the non-module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}.
706   *
707   * @return the roots in a collection, or an empty collection if none were found.
708   * @since 0.1.0
709   */
710  default List<? extends PathRoot> getSourceOutputPackages() {
711    return getPackages(StandardLocation.SOURCE_OUTPUT);
712  }
713
714  /**
715   * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs} for the
716   * given module name.
717   *
718   * @param moduleName the module name.
719   * @return the roots in a collection, or an empty collection if none were found.
720   * @since 0.1.0
721   */
722  default List<? extends PathRoot> getSourceOutputModule(String moduleName) {
723    return getModule(StandardLocation.SOURCE_OUTPUT, moduleName);
724  }
725
726
727  /**
728   * Get the module path roots for {@link StandardLocation#SOURCE_OUTPUT source outputs}.
729   *
730   * @return the roots in a map of module names to lists of roots, or an empty map if none were
731   *     found.
732   * @since 0.1.0
733   */
734  default Map<String, List<? extends PathRoot>> getSourceOutputModules() {
735    return getModules(StandardLocation.SOURCE_OUTPUT);
736  }
737
738  /**
739   * Get the path roots for the {@link StandardLocation#CLASS_PATH class path}.
740   *
741   * @return the roots in a collection, or an empty collection if none were found.
742   * @since 0.1.0
743   */
744  default List<? extends PathRoot> getClassPathPackages() {
745    return getPackages(StandardLocation.CLASS_PATH);
746  }
747
748  /**
749   * Get the path roots for the {@link StandardLocation#SOURCE_PATH source path}.
750   *
751   * @return the roots in a collection, or an empty collection if none were found.
752   * @since 0.1.0
753   */
754  default List<? extends PathRoot> getSourcePathPackages() {
755    return getPackages(StandardLocation.SOURCE_PATH);
756  }
757
758  /**
759   * Get the path roots for the
760   * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}.
761   *
762   * @return the roots in a collection, or an empty collection if none were found.
763   * @since 0.1.0
764   */
765  default List<? extends PathRoot> getAnnotationProcessorPathPackages() {
766    return getPackages(StandardLocation.ANNOTATION_PROCESSOR_PATH);
767  }
768
769  /**
770   * Get the path roots for the
771   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
772   *
773   * @param moduleName the module name to get.
774   * @return the roots in a collection, or an empty collection if none were found.
775   * @since 0.1.0
776   */
777  default List<? extends PathRoot> getAnnotationProcessorPathModule(String moduleName) {
778    return getModule(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, moduleName);
779  }
780
781  /**
782   * Get the module path roots for the
783   * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}.
784   *
785   * @return the roots in a map of module names to lists of roots, or an empty map if none were
786   *     found.
787   * @since 0.1.0
788   */
789  default Map<String, List<? extends PathRoot>> getAnnotationProcessorPathModules() {
790    return getModules(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH);
791  }
792
793  /**
794   * Get the path roots for the {@link StandardLocation#MODULE_SOURCE_PATH module source path}.
795   *
796   * @param moduleName the module name to get.
797   * @return the roots in a collection, or an empty collection if none were found.
798   * @since 0.1.0
799   */
800  default List<? extends PathRoot> getSourcePathModule(String moduleName) {
801    return getModule(StandardLocation.MODULE_SOURCE_PATH, moduleName);
802  }
803
804  /**
805   * Get the module path roots for the
806   * {@link StandardLocation#MODULE_SOURCE_PATH module source paths}.
807   *
808   * @return the roots in a map of module names to lists of roots, or an empty map if none were
809   *     found.
810   * @since 0.1.0
811   */
812  default Map<String, List<? extends PathRoot>> getSourcePathModules() {
813    return getModules(StandardLocation.MODULE_SOURCE_PATH);
814  }
815
816  /**
817   * Get the path roots for the {@link StandardLocation#MODULE_PATH module path}.
818   *
819   * @param moduleName the module name to get.
820   * @return the roots in a collection, or an empty collection if none were found.
821   * @since 0.1.0
822   */
823  default List<? extends PathRoot> getModulePathModule(String moduleName) {
824    return getModule(StandardLocation.MODULE_PATH, moduleName);
825  }
826
827  /**
828   * Get the module path roots for the {@link StandardLocation#MODULE_PATH module paths}.
829   *
830   * @return the roots in a map of module names to lists of roots, or an empty map if none were
831   *     found.
832   * @since 0.1.0
833   */
834  default Map<String, List<? extends PathRoot>> getModulePathModules() {
835    return getModules(StandardLocation.MODULE_PATH);
836  }
837}