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