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}