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