Interface PackageContainerGroup
- All Superinterfaces:
AutoCloseable
,Closeable
,ContainerGroup
- All Known Subinterfaces:
OutputContainerGroup
Warning: container group APIs are not designed to allow reuse between
compilation runs due the behaviour around providing access to class loaders. See the notes for
getClassLoader()
for more details.
- Since:
- 0.0.1
- Author:
- Ashley Scopes
-
Method Summary
Modifier and TypeMethodDescriptionvoid
addPackage
(Container container) Add a container to this group.void
addPackage
(PathRoot path) Add a path to this group.Get a class loader for this group of containers.Find the first occurrence of a given path to a file in packages or modules.getFileForInput
(String packageName, String relativeName) Get aFileObject
that can have content read from it.getFileForOutput
(String packageName, String relativeName) Get aFileObject
that can have content written to it for the given file.getJavaFileForInput
(String className, JavaFileObject.Kind kind) Get aJavaFileObject
that can have content read from it for the given file.getJavaFileForOutput
(String className, JavaFileObject.Kind kind) Get aJavaFileObject
that can have content written to it for the given class.Get the package-oriented location that this group of paths is for.Get the package containers in this group.inferBinaryName
(PathFileObject fileObject) Try to infer the binary name of a given file object.boolean
isEmpty()
Determine if this group has no paths registered.List all files recursively in this container group, returning a multimap of each container and all files within that container.listFileObjects
(String packageName, Set<? extends JavaFileObject.Kind> kinds, boolean recurse) List all the file objects that match the given criteria in this group.Methods inherited from interface io.github.ascopes.jct.containers.ContainerGroup
contains, getRelease, getServiceLoader
-
Method Details
-
addPackage
Add a container to this group.The provided container will be closed when this group is closed.
Note that this will destroy the
class loader
if one is already allocated from a previous request.- Parameters:
container
- the container to add.
-
addPackage
Add a path to this group.Note that this will destroy the
class loader
if one is already allocated from a previous request.If the path points to some form of archive (such as a JAR), then this may open that archive in a new resource internally. If this occurs, then the resource will always be freed by this class by calling
Closeable.close()
.Any other closable resources passed to this function will not be closed by this implementation. You must handle the lifecycle of those objects yourself.
- Parameters:
path
- the path to add.
-
getClassLoader
Get a class loader for this group of containers.If a class loader has not yet been created, then calling this method is expected to create a class loader first.
This method is primarily provided to allow JCT to load components like annotation processors from provided class paths dynamically during compilation, but is also suitable for use by users to load classes compiled as part of test cases into memory to perform further tests on the results via standard reflection APIs.
While not strictly required, it is recommended that any implementations of this class provide a subclass of
URLClassLoader
to ensure similar behaviour to the internals within OpenJDK'sjavac
implementation.Warning: adding additional containers to this group after accessing this class loader may result in the class loader being destroyed or re-created. This can result in confusing behaviour where classes may get loaded multiple times. Generally this shouldn't be an issue since the class loader is only accessed once the files have been added, but this does mean that container group types should not be reused between compilation runs if possible. Due to how the JCT API works, this means that you should avoid calling this method prior to invoking the compiler itself, and likewise should try to avoid adding new packages to implementations of container groups after the compiler has been invoked.
Example of usage with Java:
// Checked exception handling has been omitted from this example for // brevity. See the java.lang.reflect documentation for full details. ClassLoader cl = containerGroup.getClassLoader(); Class<?> cls = cl.loadClass("org.example.NumberAdder"); Object adder = cls.getDeclaredConstructor().newInstance(); Method addMethod = cls.getMethod("add", int.class, int.class); int result = (int) addMethod.invoke(adder, 9, 18); assertThat(result).isEqualTo(27);
Example of usage with Groovy:
// Groovy is a great option if you are writing lots of tests like this, // since it will avoid much of the boilerplate around using the reflection // APIs directly due to the ability to dynamically infer the types of // objects at runtime. You also avoid having to deal with checked exceptions. def cl = containerGroup.getClassLoader() def cls = cl.loadClass("org.example.NumberAdder") def adder = cls.getDeclaredConstructor().newInstance() def result = adder.add(9, 18) assertThat(result).isEqualTo(27)
Example working with resources:
// Checked exception handling has been omitted from this example for // brevity. See the java.lang.reflect documentation for full details. // // Consider the .getFile method on the PackageContainerGroup class instead // to achieve the same outcome with simpler syntax. ClassLoader cl = containerGroup.getClassLoader(); try (InputStream inputStream = cl.getResourceAsStream("META-INF/spring.factories")) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); inputStream.transferTo(outputStream); String content = new String(outputStream.toByteArray(), ...); ... }
- Returns:
- a class loader for the contents of this container group.
- See Also:
-
getFile
Find the first occurrence of a given path to a file in packages or modules.Paths should be relative to the root of this package group. Absolute paths will be treated as erroneous inputs.
Modules are treated as subdirectories where supported.
This method accepts multiple strings to prevent users from having to hard-code OS-specific file path separators that may create flaky tests. For example,
.getFile("foo", "bar", "baz")
is equivalent to.getFile("foo/bar/baz")
on most systems.Unlike
getClassLoader()
, this will allow access to the files directly without needing to handle class loading exceptions.// Letting JCT infer the correct path separators to use (recommended). containerGroup.getFile("foo", "bar", "baz.txt")...; // Using platform-specific separators. containerGroup.getFile("foo/bar/baz.txt")...;
- Parameters:
fragments
- parts of the path.- Returns:
- the first occurrence of the path in this group, or null if not found.
- Throws:
IllegalArgumentException
- if the provided path is absolute.IllegalArgumentException
- if no path fragments are provided.- See Also:
-
getFileForInput
Get aFileObject
that can have content read from it.This will return
null
if no file is found matching the criteria.- Parameters:
packageName
- the package name of the file to read.relativeName
- the relative name of the file to read.- Returns:
- the file object, or null if the file is not found.
-
getFileForOutput
Get aFileObject
that can have content written to it for the given file.This will attempt to write to the first writeable path in this group.
null
will be returned if no writeable paths exist in this group.- Parameters:
packageName
- the name of the package the file is in.relativeName
- the relative name of the file within the package.- Returns:
- the
FileObject
to write to, or null if this group has no paths that can be written to.
-
getJavaFileForInput
Get aJavaFileObject
that can have content read from it for the given file.This will return
null
if no file is found matching the criteria.- Parameters:
className
- the binary name of the class to read.kind
- the kind of file to read.- Returns:
- the
JavaFileObject
to write to, or null if this group has no paths that can be written to.
-
getJavaFileForOutput
Get aJavaFileObject
that can have content written to it for the given class.This will attempt to write to the first writeable path in this group.
null
will be returned if no writeable paths exist in this group.- Parameters:
className
- the name of the class.kind
- the kind of the class file.- Returns:
- the
JavaFileObject
to write to, or null if this group has no paths that can be written to.
-
getLocation
Get the package-oriented location that this group of paths is for.- Specified by:
getLocation
in interfaceContainerGroup
- Returns:
- the package-oriented location.
-
getPackages
Get the package containers in this group.Returned packages are presented in the order that they were registered. This is the resolution order that the compiler will use.
- Returns:
- the containers.
-
inferBinaryName
Try to infer the binary name of a given file object.- Parameters:
fileObject
- the file object to infer the binary name for.- Returns:
- the binary name if known, or null otherwise.
-
isEmpty
boolean isEmpty()Determine if this group has no paths registered.- Returns:
true
if no paths are registered.false
if paths are registered.
-
listFileObjects
Set<JavaFileObject> listFileObjects(String packageName, Set<? extends JavaFileObject.Kind> kinds, boolean recurse) throws IOException List all the file objects that match the given criteria in this group.File objects are returned in an unordered collection, but lookup will be performed in a deterministic order corresponding to the same order as the containers returned by
getPackages()
.- Parameters:
packageName
- the package name to look in.kinds
- the kinds of file to look for.recurse
-true
to recurse subpackages,false
to only consider the given package.- Returns:
- the file objects that were found.
- Throws:
IOException
- if the file lookup fails due to an IO error somewhere.
-
listAllFiles
List all files recursively in this container group, returning a multimap of each container and all files within that container.- Returns:
- a multimap of containers mapping to collections of all files in that container.
- Throws:
IOException
- if the file lookup fails due to an IO error somewhere.- Since:
- 0.6.0
-