Interface PackageContainerGroup

All Superinterfaces:
AutoCloseable, Closeable, ContainerGroup
All Known Subinterfaces:
OutputContainerGroup

public interface PackageContainerGroup extends ContainerGroup
Base interface representing a group of package-oriented paths.

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 Details

    • addPackage

      void addPackage(Container container)
      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

      void addPackage(PathRoot path)
      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's javac 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

      @Nullable Path getFile(String... fragments)
      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

      @Nullable PathFileObject getFileForInput(String packageName, String relativeName)
      Get a FileObject 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

      @Nullable PathFileObject getFileForOutput(String packageName, String relativeName)
      Get a FileObject 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 a JavaFileObject 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 a JavaFileObject 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 interface ContainerGroup
      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