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.filemanagers; 017 018import io.github.ascopes.jct.containers.ModuleContainerGroup; 019import io.github.ascopes.jct.containers.OutputContainerGroup; 020import io.github.ascopes.jct.containers.PackageContainerGroup; 021import io.github.ascopes.jct.workspaces.PathRoot; 022import io.github.ascopes.jct.workspaces.Workspace; 023import java.io.IOException; 024import java.util.Collection; 025import java.util.Set; 026import javax.tools.JavaFileManager; 027import javax.tools.JavaFileObject; 028import javax.tools.JavaFileObject.Kind; 029import javax.tools.StandardLocation; 030import org.apiguardian.api.API; 031import org.apiguardian.api.API.Status; 032import org.jspecify.annotations.Nullable; 033 034/** 035 * Extension around a {@link JavaFileManager} that allows adding of {@link PathRoot} objects to the 036 * manager. 037 * 038 * <p>This component is responsible for bridging the gap between a {@link Workspace} and 039 * a {@link javax.tools.JavaCompiler} when performing a compilation, and thus includes a number of 040 * required operations that the compiler will query the file system with. In addition, this 041 * interface also defines a number of additional functionalities that are useful for querying and 042 * verifying the outcome of compilations within tests. 043 * 044 * @author Ashley Scopes 045 * @since 0.0.1 046 */ 047@API(since = "0.0.1", status = Status.STABLE) 048public interface JctFileManager extends JavaFileManager { 049 050 /** 051 * Add a package-oriented path to a given location. 052 * 053 * <p>To add a module, first obtain the module location using 054 * {@link #getLocationForModule(Location, String)}, and pass that result to this call. 055 * 056 * @param location the location to use. 057 * @param path the path to add. 058 * @see #getLocationForModule(Location, String) 059 */ 060 void addPath(Location location, PathRoot path); 061 062 /** 063 * Add a collection of package-oriented paths to a given location. 064 * 065 * <p>To add a module, first obtain the module location using 066 * {@link #getLocationForModule(Location, String)}, and pass that result to this call. 067 * 068 * @param location the location to use. 069 * @param paths the paths to add. 070 * @see #getLocationForModule(Location, String) 071 */ 072 void addPaths(Location location, Collection<? extends PathRoot> paths); 073 074 /** 075 * Copy all containers from the first location to the second location. 076 * 077 * @param from the first location. 078 * @param to the second location. 079 */ 080 void copyContainers(Location from, Location to); 081 082 /** 083 * Register an empty container for the given location to indicate to the compiler that the feature 084 * exists, but has no configured paths. 085 * 086 * <p>This is needed to coerce the behaviour for annotation processing in some cases. 087 * 088 * <p>If the location already exists, then do not do anything. 089 * 090 * <p>If the location is an output location, then this operation does not make any sense, since 091 * an empty location cannot have files output to it. In this case, you will likely get an 092 * exception. 093 * 094 * <p>Likewise, this operation does not make sense for module locations within a module-oriented 095 * location group, so this operation will fail with an error for those inputs as well. 096 * 097 * @param location the location to apply an empty container for. 098 * @throws IllegalArgumentException if the location is an output location or a module location. 099 */ 100 void createEmptyLocation(Location location); 101 102 /** 103 * Get the associated effective release. 104 * 105 * @return the effective release. 106 * @since 0.0.1 107 */ 108 String getEffectiveRelease(); 109 110 /** 111 * Get the container group for the given package-oriented location. 112 * 113 * @param location the package oriented location. 114 * @return the container group, or null if one does not exist. 115 */ 116 PackageContainerGroup getPackageContainerGroup(Location location); 117 118 /** 119 * Get a collection of all package container impl in this file manager. 120 * 121 * @return the package container impl. 122 */ 123 Collection<PackageContainerGroup> getPackageContainerGroups(); 124 125 /** 126 * Get the container group for the given module-oriented location. 127 * 128 * @param location the module oriented location. 129 * @return the container group, or null if one does not exist. 130 */ 131 ModuleContainerGroup getModuleContainerGroup(Location location); 132 133 /** 134 * Get a collection of all module container impl in this file manager. 135 * 136 * @return the module container impl. 137 */ 138 Collection<ModuleContainerGroup> getModuleContainerGroups(); 139 140 /** 141 * Get the container group for the given output-oriented location. 142 * 143 * @param location the output oriented location. 144 * @return the container group, or null if one does not exist. 145 */ 146 OutputContainerGroup getOutputContainerGroup(Location location); 147 148 /** 149 * Get a collection of all output container impl in this file manager. 150 * 151 * @return the output container impl. 152 */ 153 Collection<OutputContainerGroup> getOutputContainerGroups(); 154 155 /** 156 * List all file objects in the given location matching the given criteria. 157 * 158 * @param location the location to search in. 159 * @param packageName the package name to search in, or {@code ""} to search in the root 160 * location. 161 * @param kinds the kinds of file to return, or {@link Set#of} {@code (} {@link Kind#OTHER} 162 * {@code )} to find all files. 163 * @param recurse {@code true} to recurse into subpackages, {@code false} to only check the 164 * current package. 165 * @return a collection of unique file objects that were found. 166 * @throws IOException if an IO error occurs reading the file system. 167 */ 168 @Override 169 Set<JavaFileObject> list( 170 Location location, 171 String packageName, 172 Set<Kind> kinds, 173 boolean recurse 174 ) throws IOException; 175 176 /// 177 /// Default helper overrides 178 /// 179 180 /** 181 * Get the location holding the {@link StandardLocation#CLASS_OUTPUT class outputs}. 182 * 183 * @return the location, or {@code null} if the location is not present in the file manager. 184 * @since 0.1.0 185 */ 186 @Nullable 187 default OutputContainerGroup getClassOutputGroup() { 188 return getOutputContainerGroup(StandardLocation.CLASS_OUTPUT); 189 } 190 191 /** 192 * Get the location holding the {@link StandardLocation#SOURCE_OUTPUT source outputs}. 193 * 194 * @return the location, or {@code null} if the location is not present in the file manager. 195 * @since 0.1.0 196 */ 197 @Nullable 198 default OutputContainerGroup getSourceOutputGroup() { 199 return getOutputContainerGroup(StandardLocation.SOURCE_OUTPUT); 200 } 201 202 /** 203 * Get the location holding the {@link StandardLocation#CLASS_PATH class path}. 204 * 205 * @return the location, or {@code null} if the location is not present in the file manager. 206 * @since 0.1.0 207 */ 208 @Nullable 209 default PackageContainerGroup getClassPathGroup() { 210 return getPackageContainerGroup(StandardLocation.CLASS_PATH); 211 } 212 213 /** 214 * Get the location holding the {@link StandardLocation#SOURCE_PATH source path}. 215 * 216 * @return the location, or {@code null} if the location is not present in the file manager. 217 * @since 0.1.0 218 */ 219 @Nullable 220 default PackageContainerGroup getSourcePathGroup() { 221 return getPackageContainerGroup(StandardLocation.SOURCE_PATH); 222 } 223 224 /** 225 * Get the location holding the 226 * {@link StandardLocation#ANNOTATION_PROCESSOR_PATH annotation processor path}. 227 * 228 * @return the location, or {@code null} if the location is not present in the file manager. 229 * @since 0.1.0 230 */ 231 @Nullable 232 default PackageContainerGroup getAnnotationProcessorPathGroup() { 233 return getPackageContainerGroup(StandardLocation.ANNOTATION_PROCESSOR_PATH); 234 } 235 236 /** 237 * Get the location holding the 238 * {@link StandardLocation#ANNOTATION_PROCESSOR_MODULE_PATH annotation processor module path}. 239 * 240 * @return the location, or {@code null} if the location is not present in the file manager. 241 * @since 0.1.0 242 */ 243 @Nullable 244 default ModuleContainerGroup getAnnotationProcessorModulePathGroup() { 245 return getModuleContainerGroup(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH); 246 } 247 248 /** 249 * Get the location holding the {@link StandardLocation#PLATFORM_CLASS_PATH platform class path} 250 * (also known as the boot class path). 251 * 252 * @return the location, or {@code null} if the location is not present in the file manager. 253 * @since 0.1.0 254 * @deprecated The platform class path has been mostly replaced by the use of system modules, so 255 * should not be used. This will be removed in v1.0.0. 256 */ 257 @Deprecated(forRemoval = true, since = "0.6.0") 258 @Nullable 259 @SuppressWarnings("DeprecatedIsStillUsed") 260 default PackageContainerGroup getPlatformClassPathGroup() { 261 return getPackageContainerGroup(StandardLocation.PLATFORM_CLASS_PATH); 262 } 263 264 /** 265 * Get the location holding the 266 * {@link StandardLocation#NATIVE_HEADER_OUTPUT native header outputs}. 267 * 268 * @return the location, or {@code null} if the location is not present in the file manager. 269 * @since 0.1.0 270 * @deprecated This is a specialist method that is usually not used. To keep the API simple, 271 * this is being removed. You should instead call {@link #getOutputContainerGroup(Location)}, 272 * passing {@link StandardLocation#NATIVE_HEADER_OUTPUT} as the first parameter. 273 */ 274 @Deprecated(since = "0.6.0", forRemoval = true) 275 @Nullable 276 @SuppressWarnings("DeprecatedIsStillUsed") 277 default OutputContainerGroup getNativeHeaderOutputGroup() { 278 return getOutputContainerGroup(StandardLocation.NATIVE_HEADER_OUTPUT); 279 } 280 281 /** 282 * Get the location holding the {@link StandardLocation#MODULE_SOURCE_PATH module source path}. 283 * 284 * @return the location, or {@code null} if the location is not present in the file manager. 285 * @since 0.1.0 286 */ 287 @Nullable 288 default ModuleContainerGroup getModuleSourcePathGroup() { 289 return getModuleContainerGroup(StandardLocation.MODULE_SOURCE_PATH); 290 } 291 292 /** 293 * Get the location holding the {@link StandardLocation#UPGRADE_MODULE_PATH upgrade module path}. 294 * 295 * @return the location, or {@code null} if the location is not present in the file manager. 296 * @since 0.1.0 297 * @deprecated This is a specialist method that is usually not used. To keep the API simple, 298 * this is being removed. You should instead call {@link #getModuleContainerGroup(Location)}, 299 * passing {@link StandardLocation#SYSTEM_MODULES} as the first parameter. 300 */ 301 @Deprecated(since = "0.6.0", forRemoval = true) 302 @Nullable 303 @SuppressWarnings("DeprecatedIsStillUsed") 304 default ModuleContainerGroup getUpgradeModulePathGroup() { 305 return getModuleContainerGroup(StandardLocation.UPGRADE_MODULE_PATH); 306 } 307 308 /** 309 * Get the location holding the {@link StandardLocation#SYSTEM_MODULES system modules}. 310 * 311 * @return the location, or {@code null} if the location is not present in the file manager. 312 * @since 0.1.0 313 * @deprecated This is a specialist method that is usually not used. To keep the API simple, 314 * this is being removed. You should instead call {@link #getModuleContainerGroup(Location)}, 315 * passing {@link StandardLocation#SYSTEM_MODULES} as the first parameter. 316 */ 317 @Deprecated(since = "0.6.0", forRemoval = true) 318 @Nullable 319 @SuppressWarnings("DeprecatedIsStillUsed") 320 default ModuleContainerGroup getSystemModulesGroup() { 321 return getModuleContainerGroup(StandardLocation.SYSTEM_MODULES); 322 } 323 324 /** 325 * Get the location holding the {@link StandardLocation#MODULE_PATH module path}. 326 * 327 * @return the location, or {@code null} if the location is not present in the file manager. 328 * @since 0.1.0 329 */ 330 @Nullable 331 default ModuleContainerGroup getModulePathGroup() { 332 return getModuleContainerGroup(StandardLocation.MODULE_PATH); 333 } 334 335 /** 336 * Get the location holding the {@link StandardLocation#PATCH_MODULE_PATH patch module path}. 337 * 338 * @return the location, or {@code null} if the location is not present in the file manager. 339 * @since 0.1.0 340 * @deprecated This is a specialist method that is usually not used. To keep the API simple, 341 * this is being removed. You should instead call {@link #getModuleContainerGroup(Location)}, 342 * passing {@link StandardLocation#PATCH_MODULE_PATH} as the first parameter. 343 */ 344 @Deprecated(since = "0.6.0", forRemoval = true) 345 @Nullable 346 @SuppressWarnings("DeprecatedIsStillUsed") 347 default ModuleContainerGroup getPatchModulePathGroup() { 348 return getModuleContainerGroup(StandardLocation.PATCH_MODULE_PATH); 349 } 350}