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