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.containers;
017
018import io.github.ascopes.jct.filemanagers.PathFileObject;
019import io.github.ascopes.jct.workspaces.PathRoot;
020import java.io.Closeable;
021import java.io.IOException;
022import java.lang.module.ModuleFinder;
023import java.nio.file.Path;
024import java.util.Collection;
025import java.util.Set;
026import javax.tools.FileObject;
027import javax.tools.JavaFileManager.Location;
028import javax.tools.JavaFileObject;
029import javax.tools.JavaFileObject.Kind;
030import org.jspecify.annotations.Nullable;
031
032/**
033 * Container that wraps a file path source of some description.
034 *
035 * <p>Closing this resource will close any internally held resources that were opened internally.
036 * Already-opened resources passed to the implementation will not be closed.
037 *
038 * @author Ashley Scopes
039 */
040public interface Container extends Closeable {
041
042  /**
043   * Determine if this container contains the path file object.
044   *
045   * @param fileObject the file object to look up.
046   * @return {@code true} if the object exists in this container, or {@code false} otherwise.
047   */
048  boolean contains(PathFileObject fileObject);
049
050  /**
051   * Find the physical path to a given string file path.
052   *
053   * <p>Examples:
054   *
055   * <pre><code>
056   *   // Using platform-specific separators ('/' for in-memory file systems).
057   *   container.getFile("foo/bar/baz.txt");
058   *
059   *   // Letting JCT infer the correct path separators to use.
060   *   container.getFile("foo", "bar", "baz.txt");
061   * </code></pre>
062   *
063   * @param fragments parts of the file name to find.
064   * @return the path if the file exists, or null if it does not exist.
065   * @throws NullPointerException     if any of the path fragments are {@code null}.
066   * @throws IllegalArgumentException if there are no path fragments provided.
067   */
068  @Nullable
069  Path getFile(String... fragments);
070
071  /**
072   * Get a {@link FileObject} for reading, if it exists.
073   *
074   * <p>If the file does not exist, {@code null} is returned.
075   *
076   * @param packageName  the package name of the file.
077   * @param relativeName the relative name of the file in the package.
078   * @return the file object, or null if it does not exist.
079   */
080  @Nullable
081  PathFileObject getFileForInput(String packageName, String relativeName);
082
083  /**
084   * Get a {@link FileObject} for writing.
085   *
086   * <p>If the container is read-only, {@code null} is returned.
087   *
088   * @param packageName  the package name of the file.
089   * @param relativeName the relative name of the file in the package.
090   * @return the file object, or null if this container is read-only.
091   */
092  @Nullable
093  PathFileObject getFileForOutput(String packageName, String relativeName);
094
095  /**
096   * Get the inner path root of the container.
097   *
098   * <p>This will usually be the same as the {@link #getPathRoot() path root}, but for archives,
099   * this will point to the root directory of the archive for the virtual file system it is loaded
100   * in, rather than the location of the archive on the original file system.
101   *
102   * <p>It is worth noting that this operation may result in the archive being loaded into memory
103   * eagerly if it uses lazy loading, due to the need to load the archive to be able to determine an
104   * accurate handle to the inner root directory.
105   *
106   * @return the path root.
107   * @since 0.0.6
108   */
109  PathRoot getInnerPathRoot();
110
111  /**
112   * Get a {@link JavaFileObject} for reading, if it exists.
113   *
114   * <p>If the file does not exist, {@code null} is returned.
115   *
116   * @param className the binary name of the class to open.
117   * @param kind      the kind of file to open.
118   * @return the file object, or null if it does not exist.
119   */
120  @Nullable
121  PathFileObject getJavaFileForInput(String className, Kind kind);
122
123  /**
124   * Get a {@link JavaFileObject} for writing.
125   *
126   * <p>If the container is read-only, {@code null} is returned.
127   *
128   * @param className the binary name of the class to open.
129   * @param kind      the kind of file to open.
130   * @return the file object, or null if this container is read-only.
131   */
132  @Nullable
133  PathFileObject getJavaFileForOutput(String className, Kind kind);
134
135  /**
136   * Get the location of this container.
137   *
138   * @return the location.
139   */
140  Location getLocation();
141
142  /**
143   * Get a module finder for this container.
144   *
145   * <p>Note that this will not detect modules that are not yet compiled.
146   *
147   * @return the module finder for this container, or {@code null} if not relevant to the
148   *     implementation.
149   */
150  @Nullable
151  ModuleFinder getModuleFinder();
152
153  /**
154   * Get the name of this container for human consumption.
155   *
156   * @return the container name.
157   */
158  String getName();
159
160  /**
161   * Get the path root of the container.
162   *
163   * @return the path root.
164   */
165  PathRoot getPathRoot();
166
167  /**
168   * Infer the binary name of a given Java file object.
169   *
170   * @param javaFileObject the Java file object to infer the binary name of.
171   * @return the name, or null if the file does not exist in this container.
172   */
173  @Nullable
174  String inferBinaryName(PathFileObject javaFileObject);
175
176  /**
177   * List all files within this container.
178   *
179   * @return all files within the container, relative to the container base path.
180   * @throws IOException if the file system failed to be read.
181   */
182  Collection<Path> listAllFiles() throws IOException;
183
184  /**
185   * List all the file objects that match the given criteria in this group.
186   *
187   * <p>The results are filled into a given collection, since this call may be made many times
188   * per compilation, and this reduces the memory overhead needed in such cases.
189   *
190   * @param packageName the package name to look in.
191   * @param kinds       the kinds of file to look for. Set to {@code Set.of(Kind.OTHER)} to find all
192   *                    types of file.
193   * @param recurse     {@code true} to recurse subpackages, {@code false} to only consider the
194   *                    given package.
195   * @param collection  the collection to fill.
196   * @throws IOException if the file system failed to be read.
197   */
198  void listFileObjects(
199      String packageName,
200      Set<? extends Kind> kinds,
201      boolean recurse,
202      Collection<JavaFileObject> collection
203  ) throws IOException;
204}