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.containers;
017
018import io.github.ascopes.jct.filemanagers.PathFileObject;
019import io.github.ascopes.jct.workspaces.PathRoot;
020import java.io.IOException;
021import java.nio.file.Path;
022import java.util.List;
023import java.util.Set;
024import javax.tools.FileObject;
025import javax.tools.JavaFileManager.Location;
026import javax.tools.JavaFileObject;
027import javax.tools.JavaFileObject.Kind;
028import org.apiguardian.api.API;
029import org.apiguardian.api.API.Status;
030
031/**
032 * Base interface representing a group of package-oriented paths.
033 *
034 * @author Ashley Scopes
035 * @since 0.0.1
036 */
037@API(since = "0.0.1", status = Status.STABLE)
038public interface PackageContainerGroup extends ContainerGroup {
039
040  /**
041   * Add a container to this group.
042   *
043   * <p>The provided container will be closed when this group is closed.
044   *
045   * @param container the container to add.
046   */
047  void addPackage(Container container);
048
049  /**
050   * Add a path to this group.
051   *
052   * <p>Note that this will destroy the {@link #getClassLoader() classloader} if one is already
053   * allocated.
054   *
055   * <p>If the path points to some form of archive (such as a JAR), then this may open that archive
056   * in a new resource internally. If this occurs, then the resource will always be freed by this
057   * class by calling {@link #close()}.
058   *
059   * <p>Any other closable resources passed to this function will not be closed by this
060   * implementation. You must handle the lifecycle of those objects yourself.
061   *
062   * @param path the path to add.
063   */
064  void addPackage(PathRoot path);
065
066  /**
067   * Get a class loader for this group of containers.
068   *
069   * <p>Note that adding additional containers to this group after accessing this class loader
070   * may result in the class loader being destroyed or re-created.
071   *
072   * @return the class loader.
073   */
074  ClassLoader getClassLoader();
075
076  /**
077   * Find the first occurrence of a given path to a file in packages or modules.
078   *
079   * <p>Modules are treated as subdirectories.
080   *
081   * <pre><code>
082   *   // Using platform-specific separators.
083   *   containerGroup.getFile("foo/bar/baz.txt")...;
084   *
085   *   // Letting JCT infer the correct path separators to use (recommended).
086   *   containerGroup.getFile("foo", "bar", "baz.txt");
087   * </code></pre>
088   *
089   * @param fragment  the first part of the path.
090   * @param fragments any additional parts of the path.
091   * @return the first occurrence of the path in this group, or null if not found.
092   * @throws IllegalArgumentException if the provided path is absolute.
093   */
094  Path getFile(String fragment, String... fragments);
095
096  /**
097   * Get a {@link FileObject} that can have content read from it.
098   *
099   * <p>This will return {@code null} if no file is found matching the criteria.
100   *
101   * @param packageName  the package name of the file to read.
102   * @param relativeName the relative name of the file to read.
103   * @return the file object, or null if the file is not found.
104   */
105  PathFileObject getFileForInput(String packageName, String relativeName);
106
107  /**
108   * Get a {@link FileObject} that can have content written to it for the given file.
109   *
110   * <p>This will attempt to write to the first writeable path in this group. {@code null}
111   * will be returned if no writeable paths exist in this group.
112   *
113   * @param packageName  the name of the package the file is in.
114   * @param relativeName the relative name of the file within the package.
115   * @return the {@link FileObject} to write to, or null if this group has no paths that can be
116   *     written to.
117   */
118  PathFileObject getFileForOutput(String packageName, String relativeName);
119
120  /**
121   * Get a {@link JavaFileObject} that can have content read from it for the given file.
122   *
123   * <p>This will return {@code null} if no file is found matching the criteria.
124   *
125   * @param className the binary name of the class to read.
126   * @param kind      the kind of file to read.
127   * @return the {@link JavaFileObject} to write to, or null if this group has no paths that can be
128   *     written to.
129   */
130  PathFileObject getJavaFileForInput(String className, Kind kind);
131
132  /**
133   * Get a {@link JavaFileObject} that can have content written to it for the given class.
134   *
135   * <p>This will attempt to write to the first writeable path in this group. {@code null}
136   * will be returned if no writeable paths exist in this group.
137   *
138   * @param className the name of the class.
139   * @param kind      the kind of the class file.
140   * @return the {@link JavaFileObject} to write to, or null if this group has no paths that can be
141   *     written to.
142   */
143  PathFileObject getJavaFileForOutput(String className, Kind kind);
144
145  /**
146   * Get the package-oriented location that this group of paths is for.
147   *
148   * @return the package-oriented location.
149   */
150  Location getLocation();
151
152  /**
153   * Get the package containers in this group.
154   *
155   * <p>Returned packages are presented in the order that they were registered. This is the
156   * resolution order that the compiler will use.
157   *
158   * @return the containers.
159   */
160  List<Container> getPackages();
161
162  /**
163   * Try to infer the binary name of a given file object.
164   *
165   * @param fileObject the file object to infer the binary name for.
166   * @return the binary name if known, or null otherwise.
167   */
168  String inferBinaryName(PathFileObject fileObject);
169
170  /**
171   * Determine if this group has no paths registered.
172   *
173   * @return {@code true} if no paths are registered. {@code false} if paths are registered.
174   */
175  boolean isEmpty();
176
177  /**
178   * List all the file objects that match the given criteria in this group.
179   *
180   * @param packageName the package name to look in.
181   * @param kinds       the kinds of file to look for.
182   * @param recurse     {@code true} to recurse subpackages, {@code false} to only consider the
183   *                    given package.
184   * @return thr file objects that were found.
185   * @throws IOException if the file lookup fails due to an IO exception.
186   */
187  Set<JavaFileObject> listFileObjects(
188      String packageName,
189      Set<? extends Kind> kinds,
190      boolean recurse
191  ) throws IOException;
192}