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