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.IOException;
021import java.nio.file.Path;
022import java.util.Collection;
023import java.util.Set;
024import javax.annotation.Nullable;
025import javax.annotation.WillCloseWhenClosed;
026import javax.annotation.WillNotClose;
027import javax.tools.FileObject;
028import javax.tools.JavaFileManager.Location;
029import javax.tools.JavaFileObject;
030import javax.tools.JavaFileObject.Kind;
031import org.apiguardian.api.API;
032import org.apiguardian.api.API.Status;
033
034/**
035 * Base interface representing a group of package-oriented paths.
036 *
037 * @author Ashley Scopes
038 * @since 0.0.1
039 */
040@API(since = "0.0.1", status = Status.EXPERIMENTAL)
041public interface PackageContainerGroup extends ContainerGroup {
042
043  /**
044   * Add a container to this group.
045   *
046   * <p>The provided container will be closed when this group is closed.
047   *
048   * @param container the container to add.
049   */
050  void addPackage(@WillCloseWhenClosed Container container);
051
052  /**
053   * Add a path to this group.
054   *
055   * <p>Note that this will destroy the {@link #getClassLoader() classloader} if one is already
056   * allocated.
057   *
058   * <p>If the path points to some form of archive (such as a JAR), then this may open that archive
059   * in a new resource internally. If this occurs, then the resource will always be freed by this
060   * class by calling {@link #close()}.
061   *
062   * <p>Any other closable resources passed to this function will not be closed by this
063   * implementation. You must handle the lifecycle of those objects yourself.
064   *
065   * @param path the path to add.
066   */
067  void addPackage(@WillNotClose PathRoot path);
068
069  /**
070   * Get a class loader for this group of containers.
071   *
072   * <p>Note that adding additional containers to this group after accessing this class loader
073   * may result in the class loader being destroyed or re-created.
074   *
075   * @return the class loader.
076   */
077  ClassLoader getClassLoader();
078
079  /**
080   * Find the first occurrence of a given path to a file in packages or modules.
081   *
082   * <p>Modules are treated as subdirectories.
083   *
084   * @param fragment  the first part of the path.
085   * @param fragments any additional parts of the path.
086   * @return the first occurrence of the path in this group, or null if not found.
087   * @throws IllegalArgumentException if the provided path is absolute.
088   */
089  @Nullable
090  Path getFile(String fragment, String... fragments);
091
092  /**
093   * Get a {@link FileObject} that can have content read from it.
094   *
095   * <p>This will return {@code null} if no file is found matching the criteria.
096   *
097   * @param packageName  the package name of the file to read.
098   * @param relativeName the relative name of the file to read.
099   * @return the file object, or null if the file is not found.
100   */
101  @Nullable
102  PathFileObject getFileForInput(String packageName, String relativeName);
103
104  /**
105   * Get a {@link FileObject} that can have content written to it for the given file.
106   *
107   * <p>This will attempt to write to the first writeable path in this group. {@code null}
108   * will be returned if no writeable paths exist in this group.
109   *
110   * @param packageName  the name of the package the file is in.
111   * @param relativeName the relative name of the file within the package.
112   * @return the {@link FileObject} to write to, or null if this group has no paths that can be
113   *     written to.
114   */
115  @Nullable
116  PathFileObject getFileForOutput(String packageName, String relativeName);
117
118  /**
119   * Get a {@link JavaFileObject} that can have content read from it for the given file.
120   *
121   * <p>This will return {@code null} if no file is found matching the criteria.
122   *
123   * @param className the binary name of the class to read.
124   * @param kind      the kind of file to read.
125   * @return the {@link JavaFileObject} to write to, or null if this group has no paths that can be
126   *     written to.
127   */
128  @Nullable
129  PathFileObject getJavaFileForInput(String className, Kind kind);
130
131  /**
132   * Get a {@link JavaFileObject} that can have content written to it for the given class.
133   *
134   * <p>This will attempt to write to the first writeable path in this group. {@code null}
135   * will be returned if no writeable paths exist in this group.
136   *
137   * @param className the name of the class.
138   * @param kind      the kind of the class file.
139   * @return the {@link JavaFileObject} to write to, or null if this group has no paths that can be
140   *     written to.
141   */
142  @Nullable
143  PathFileObject getJavaFileForOutput(String className, Kind kind);
144
145  /**
146   * Get the package-oriented location that this group of paths is for.
147   *
148   * @return the package-oriented location.
149   */
150  Location getLocation();
151
152  /**
153   * Get the package containers in this group.
154   *
155   * @return the containers.
156   */
157  Collection<Container> getPackages();
158
159  /**
160   * Try to infer the binary name of a given file object.
161   *
162   * @param fileObject the file object to infer the binary name for.
163   * @return the binary name if known, or null otherwise.
164   */
165  @Nullable
166  String inferBinaryName(PathFileObject fileObject);
167
168  /**
169   * Determine if this group has no paths registered.
170   *
171   * @return {@code true} if no paths are registered. {@code false} if paths are registered.
172   */
173  boolean isEmpty();
174
175  /**
176   * List all the file objects that match the given criteria in this group.
177   *
178   * @param packageName the package name to look in.
179   * @param kinds       the kinds of file to look for.
180   * @param recurse     {@code true} to recurse subpackages, {@code false} to only consider the
181   *                    given package.
182   * @param collection  the collection to fill.
183   * @throws IOException if the file lookup fails due to an IO exception.
184   */
185  void listFileObjects(
186      String packageName,
187      Set<? extends Kind> kinds,
188      boolean recurse,
189      Collection<JavaFileObject> collection
190  ) throws IOException;
191}