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