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, if the information is not available, or if an error
128   *     occurred obtaining the information.
129   */
130  @Override
131  long getLastModified();
132
133  /**
134   * Get the location of this path file object.
135   *
136   * @return the location.
137   */
138  Location getLocation();
139
140  /**
141   * Get the file name as a string.
142   *
143   * @return the name of the file.
144   */
145  @Override
146  String getName();
147
148  /**
149   * Determine the class nesting kind, where appropriate.
150   *
151   * <p>In most implementations, this method will always return {@code null}, since this
152   * information is not readily available without preloading the file in question and parsing it
153   * first.
154   *
155   * <p>At the time of writing, the OpenJDK implementations of the JavaFileObject class
156   * do not provide an implementation for this method either.
157   *
158   * @return the nesting kind, or {@code null} if the information is not available.
159   */
160  @Nullable
161  @Override
162  default NestingKind getNestingKind() {
163    return null;
164  }
165
166  /**
167   * Get the relative path of this file object.
168   *
169   * @return the path of this file object.
170   */
171  Path getRelativePath();
172
173  /**
174   * Get the root path that the package containing this file is nested within.
175   *
176   * @return the root path.
177   */
178  Path getRootPath();
179
180  /**
181   * Determine the hash code for this object.
182   *
183   * @return the hash code for the object.
184   */
185  @Override
186  int hashCode();
187
188  /**
189   * Determine if a given simple name and file kind are compatible with this file object.
190   *
191   * <p>This will perform a case-sensitive check, regardless of the platform that it runs on.
192   *
193   * @param simpleName the simple name of the class to compare to this file.
194   * @param kind       the kind of the class to compare to this file.
195   * @return {@code true} if the simple name and kind are compatible with the current file object
196   *     name, or {@code false} if not.
197   */
198  @Override
199  boolean isNameCompatible(String simpleName, Kind kind);
200
201  /**
202   * Open an input stream into this file.
203   *
204   * <p>This input stream must be closed once finished with, otherwise resources will be leaked.
205   *
206   * <p>The returned implementation will always be buffered when appropriate.
207   *
208   * @return an input stream.
209   * @throws NoSuchFileException if the file does not exist.
210   * @throws IOException         if an IO error occurs.
211   */
212  @Override
213  InputStream openInputStream() throws IOException;
214
215  /**
216   * Open an output stream to this file.
217   *
218   * <p>This will create the file first if it does not already exist. If it does exist, then this
219   * will overwrite the file and truncate it. The parent directories will also be created if they do
220   * not exist.
221   *
222   * <p>This output stream must be closed once finished with, otherwise resources will be leaked.
223   *
224   * <p>The returned implementation will always be buffered when appropriate.
225   *
226   * @return an output stream.
227   * @throws IOException if an IO error occurs.
228   */
229  @Override
230  OutputStream openOutputStream() throws IOException;
231
232  /**
233   * Open a reader to this file using the default charset (UTF-8).
234   *
235   * <p>This reader must be closed once finished with, otherwise resources will be leaked.
236   *
237   * <p>The returned implementation will always be buffered when appropriate.
238   *
239   * @param ignoreEncodingErrors {@code true} to suppress encoding errors, or {@code false} to throw
240   *                             them to the caller.
241   * @return a reader.
242   * @throws NoSuchFileException if the file does not exist.
243   * @throws IOException         if an IO error occurs.
244   */
245  @Override
246  Reader openReader(boolean ignoreEncodingErrors) throws IOException;
247
248  /**
249   * Open a writer to this file using the default charset (UTF-8).
250   *
251   * <p>This will create the file first if it does not already exist. If it does exist, this will
252   * first overwrite the file and truncate it. The parent directories will also be created if they
253   * do not exist.
254   *
255   * <p>This input stream must be closed once finished with, otherwise resources will be leaked.
256   *
257   * <p>The returned implementation will always be buffered when appropriate.
258   *
259   * @return a writer.
260   * @throws IOException if an IO error occurs.
261   */
262  @Override
263  Writer openWriter() throws IOException;
264
265  /**
266   * Get a string representation of this object.
267   *
268   * @return a string representation of this object.
269   */
270  @Override
271  String toString();
272
273  /**
274   * Determine the URI for this file.
275   *
276   * @return the URI for this file object.
277   */
278  @Override
279  URI toUri();
280}