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.filemanagers;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.io.OutputStream;
021import java.io.Reader;
022import java.io.Writer;
023import java.net.URI;
024import java.nio.file.NoSuchFileException;
025import java.nio.file.Path;
026import javax.lang.model.element.Modifier;
027import javax.lang.model.element.NestingKind;
028import javax.tools.FileObject;
029import javax.tools.JavaFileManager.Location;
030import javax.tools.JavaFileObject;
031import org.apiguardian.api.API;
032import org.apiguardian.api.API.Status;
033import org.jspecify.annotations.Nullable;
034
035/**
036 * Interface for a path-based {@link JavaFileObject} that points to a path on a file system
037 * somewhere.
038 *
039 * <p>This object will always use UTF-8 encoding when obtaining readers or writers.
040 *
041 * <p>No access level or nesting kind information is provided by this implementation.
042 *
043 * @author Ashley Scopes
044 * @since 1.0.0
045 */
046@API(since = "1.0.0", status = Status.STABLE)
047public interface PathFileObject extends JavaFileObject {
048
049  /**
050   * Timestamp that is returned if the file object has never been modified.
051   */
052  long NOT_MODIFIED = 0L;
053
054  /**
055   * Attempt to delete the file.
056   *
057   * @return {@code true} if the file was deleted, or {@code false} if it was not deleted.
058   */
059  @Override
060  boolean delete();
061
062  /**
063   * Determine if this object equals another.
064   *
065   * @param other the other object to compare with.
066   * @return {@code true} if the other object is also a {@link FileObject} and has the same
067   *     {@link #toUri() URI} as this object, or {@code false} otherwise.
068   */
069  @Override
070  boolean equals(@Nullable Object other);
071
072  /**
073   * Get the absolute path of this file object.
074   *
075   * @return the full path.
076   */
077  Path getAbsolutePath();
078
079  /**
080   * Get the class access level, where appropriate.
081   *
082   * <p>In most implementations, this class will always return {@code null}, since this
083   * information is not readily available without preloading the file in question and parsing it
084   * first.
085   *
086   * <p>At the time of writing, the OpenJDK implementations of the JavaFileObject class
087   * do not provide an implementation for this method either.
088   *
089   * @return the modifier for the access level, or {@code null} if the information is not available.
090   */
091  @Nullable
092  @Override
093  default Modifier getAccessLevel() {
094    return null;
095  }
096
097  /**
098   * Get the inferred binary name of the file object.
099   *
100   * @return the inferred binary name.
101   */
102  String getBinaryName();
103
104  /**
105   * Read the character content of the file into memory and decode it using the default character
106   * set.
107   *
108   * @param ignoreEncodingErrors ignore encoding errors if {@code true}, or throw them otherwise.
109   * @return the character content, encoded as UTF-8.
110   * @throws IOException if an IO error occurs.
111   */
112  @Override
113  String getCharContent(boolean ignoreEncodingErrors) throws IOException;
114
115  /**
116   * Get the kind of the file.
117   *
118   * @return the inferred file kind.
119   */
120  @Override
121  Kind getKind();
122
123  /**
124   * Determine when the file was last modified.
125   *
126   * @return the timestamp in milliseconds since UNIX epoch that the file was last modified at, or
127   *     {@link #NOT_MODIFIED} if unmodified or if the information is not available.
128   */
129  @Override
130  long getLastModified();
131
132  /**
133   * Get the location of this path file object.
134   *
135   * @return the location.
136   */
137  Location getLocation();
138
139  /**
140   * Get the file name as a string.
141   *
142   * @return the name of the file.
143   */
144  @Override
145  String getName();
146
147  /**
148   * Determine the class nesting kind, where appropriate.
149   *
150   * <p>In most implementations, this method will always return {@code null}, since this
151   * information is not readily available without preloading the file in question and parsing it
152   * first.
153   *
154   * <p>At the time of writing, the OpenJDK implementations of the JavaFileObject class
155   * do not provide an implementation for this method either.
156   *
157   * @return the nesting kind, or {@code null} if the information is not available.
158   */
159  @Nullable
160  @Override
161  default NestingKind getNestingKind() {
162    return null;
163  }
164
165  /**
166   * Get the relative path of this file object.
167   *
168   * @return the path of this file object.
169   */
170  Path getRelativePath();
171
172  /**
173   * Get the root path that the package containing this file is nested within.
174   *
175   * @return the root path.
176   */
177  Path getRootPath();
178
179  /**
180   * Determine the hash code for this object.
181   *
182   * @return the hash code for the object.
183   */
184  @Override
185  int hashCode();
186
187  /**
188   * Determine if a given simple name and file kind are compatible with this file object.
189   *
190   * <p>This will perform a case-sensitive check, regardless of the platform that it runs on.
191   *
192   * @param simpleName the simple name of the class to compare to this file.
193   * @param kind       the kind of the class to compare to this file.
194   * @return {@code true} if the simple name and kind are compatible with the current file object
195   *     name, or {@code false} if not.
196   */
197  @Override
198  boolean isNameCompatible(String simpleName, Kind kind);
199
200  /**
201   * Open an input stream into this file.
202   *
203   * <p>This input stream must be closed once finished with, otherwise resources will be leaked.
204   *
205   * <p>The returned implementation will always be buffered when appropriate.
206   *
207   * @return an input stream.
208   * @throws NoSuchFileException if the file does not exist.
209   * @throws IOException         if an IO error occurs.
210   */
211  @Override
212  InputStream openInputStream() throws IOException;
213
214  /**
215   * Open an output stream to this file.
216   *
217   * <p>This will create the file first if it does not already exist. If it does exist, then this
218   * will overwrite the file and truncate it. The parent directories will also be created if they do
219   * not exist.
220   *
221   * <p>This output stream must be closed once finished with, otherwise resources will be leaked.
222   *
223   * <p>The returned implementation will always be buffered when appropriate.
224   *
225   * @return an output stream.
226   * @throws IOException if an IO error occurs.
227   */
228  @Override
229  OutputStream openOutputStream() throws IOException;
230
231  /**
232   * Open a reader to this file using the default charset (UTF-8).
233   *
234   * <p>This reader must be closed once finished with, otherwise resources will be leaked.
235   *
236   * <p>The returned implementation will always be buffered when appropriate.
237   *
238   * @param ignoreEncodingErrors {@code true} to suppress encoding errors, or {@code false} to throw
239   *                             them to the caller.
240   * @return a reader.
241   * @throws NoSuchFileException if the file does not exist.
242   * @throws IOException         if an IO error occurs.
243   */
244  @Override
245  Reader openReader(boolean ignoreEncodingErrors) throws IOException;
246
247  /**
248   * Open a writer to this file using the default charset (UTF-8).
249   *
250   * <p>This will create the file first if it does not already exist. If it does exist, this will
251   * first overwrite the file and truncate it. The parent directories will also be created if they
252   * do not exist.
253   *
254   * <p>This input stream must be closed once finished with, otherwise resources will be leaked.
255   *
256   * <p>The returned implementation will always be buffered when appropriate.
257   *
258   * @return a writer.
259   * @throws IOException if an IO error occurs.
260   */
261  @Override
262  Writer openWriter() throws IOException;
263
264  /**
265   * Get a string representation of this object.
266   *
267   * @return a string representation of this object.
268   */
269  @Override
270  String toString();
271
272  /**
273   * Determine the URI for this file.
274   *
275   * @return the URI for this file object.
276   */
277  @Override
278  URI toUri();
279}