• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.commons.io.monitor;
18 
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.Serializable;
22 import java.nio.file.Files;
23 import java.nio.file.attribute.FileTime;
24 import java.util.Objects;
25 
26 import org.apache.commons.io.FileUtils;
27 import org.apache.commons.io.file.attribute.FileTimes;
28 
29 /**
30  * The state of a file or directory, capturing the following {@link File} attributes at a point in time.
31  * <ul>
32  *   <li>File Name (see {@link File#getName()})</li>
33  *   <li>Exists - whether the file exists or not (see {@link File#exists()})</li>
34  *   <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li>
35  *   <li>Last Modified Date/Time (see {@link FileUtils#lastModifiedUnchecked(File)})</li>
36  *   <li>Length (see {@link File#length()}) - directories treated as zero</li>
37  *   <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li>
38  * </ul>
39  *
40  * <h2>Custom Implementations</h2>
41  * <p>
42  * If the state of additional {@link File} attributes is required then create a custom
43  * {@link FileEntry} with properties for those attributes. Override the
44  * {@link #newChildInstance(File)} to return a new instance of the appropriate type.
45  * You may also want to override the {@link #refresh(File)} method.
46  * </p>
47  * @see FileAlterationObserver
48  * @since 2.0
49  */
50 public class FileEntry implements Serializable {
51 
52     private static final long serialVersionUID = -2505664948818681153L;
53 
54     static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {};
55 
56     private final FileEntry parent;
57     private FileEntry[] children;
58     private final File file;
59     private String name;
60     private boolean exists;
61     private boolean directory;
62     private SerializableFileTime lastModified = SerializableFileTime.EPOCH;
63     private long length;
64 
65     /**
66      * Constructs a new monitor for a specified {@link File}.
67      *
68      * @param file The file being monitored
69      */
FileEntry(final File file)70     public FileEntry(final File file) {
71         this(null, file);
72     }
73 
74     /**
75      * Constructs a new monitor for a specified {@link File}.
76      *
77      * @param parent The parent
78      * @param file The file being monitored
79      */
FileEntry(final FileEntry parent, final File file)80     public FileEntry(final FileEntry parent, final File file) {
81         this.file = Objects.requireNonNull(file, "file");
82         this.parent = parent;
83         this.name = file.getName();
84     }
85 
86     /**
87      * Gets the directory's files.
88      *
89      * @return This directory's files or an empty
90      * array if the file is not a directory or the
91      * directory is empty
92      */
getChildren()93     public FileEntry[] getChildren() {
94         return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
95     }
96 
97     /**
98      * Gets the file being monitored.
99      *
100      * @return the file being monitored
101      */
getFile()102     public File getFile() {
103         return file;
104     }
105 
106     /**
107      * Gets the last modified time from the last time it
108      * was checked.
109      *
110      * @return the last modified time in milliseconds.
111      */
getLastModified()112     public long getLastModified() {
113         return lastModified.toMillis();
114     }
115 
116     /**
117      * Gets the last modified time from the last time it was checked.
118      *
119      * @return the last modified time.
120      * @since 2.12.0
121      */
getLastModifiedFileTime()122     public FileTime getLastModifiedFileTime() {
123         return lastModified.unwrap();
124     }
125 
126     /**
127      * Gets the length.
128      *
129      * @return the length
130      */
getLength()131     public long getLength() {
132         return length;
133     }
134 
135     /**
136      * Gets the level
137      *
138      * @return the level
139      */
getLevel()140     public int getLevel() {
141         return parent == null ? 0 : parent.getLevel() + 1;
142     }
143 
144     /**
145      * Gets the file name.
146      *
147      * @return the file name
148      */
getName()149     public String getName() {
150         return name;
151     }
152 
153     /**
154      * Gets the parent entry.
155      *
156      * @return the parent entry
157      */
getParent()158     public FileEntry getParent() {
159         return parent;
160     }
161 
162     /**
163      * Tests whether the file is a directory or not.
164      *
165      * @return whether the file is a directory or not
166      */
isDirectory()167     public boolean isDirectory() {
168         return directory;
169     }
170 
171     /**
172      * Tests whether the file existed the last time it
173      * was checked.
174      *
175      * @return whether the file existed
176      */
isExists()177     public boolean isExists() {
178         return exists;
179     }
180 
181     /**
182      * Creates a new child instance.
183      * <p>
184      * Custom implementations should override this method to return
185      * a new instance of the appropriate type.
186      * </p>
187      *
188      * @param file The child file
189      * @return a new child instance
190      */
newChildInstance(final File file)191     public FileEntry newChildInstance(final File file) {
192         return new FileEntry(this, file);
193     }
194 
195     /**
196      * Refreshes the attributes from the {@link File}, indicating
197      * whether the file has changed.
198      * <p>
199      * This implementation refreshes the {@code name}, {@code exists},
200      * {@code directory}, {@code lastModified} and {@code length}
201      * properties.
202      * </p>
203      * <p>
204      * The {@code exists}, {@code directory}, {@code lastModified}
205      * and {@code length} properties are compared for changes
206      * </p>
207      *
208      * @param file the file instance to compare to
209      * @return {@code true} if the file has changed, otherwise {@code false}
210      */
refresh(final File file)211     public boolean refresh(final File file) {
212         // cache original values
213         final boolean origExists = exists;
214         final SerializableFileTime origLastModified = lastModified;
215         final boolean origDirectory = directory;
216         final long origLength = length;
217 
218         // refresh the values
219         name = file.getName();
220         exists = Files.exists(file.toPath());
221         directory = exists && file.isDirectory();
222         try {
223             setLastModified(exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH);
224         } catch (final IOException e) {
225             setLastModified(SerializableFileTime.EPOCH);
226         }
227         length = exists && !directory ? file.length() : 0;
228 
229         // Return if there are changes
230         return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory
231             || length != origLength;
232     }
233 
234     /**
235      * Sets the directory's files.
236      *
237      * @param children This directory's files, may be null
238      */
setChildren(final FileEntry... children)239     public void setChildren(final FileEntry... children) {
240         this.children = children;
241     }
242 
243     /**
244      * Sets whether the file is a directory or not.
245      *
246      * @param directory whether the file is a directory or not
247      */
setDirectory(final boolean directory)248     public void setDirectory(final boolean directory) {
249         this.directory = directory;
250     }
251 
252     /**
253      * Sets whether the file existed the last time it
254      * was checked.
255      *
256      * @param exists whether the file exists or not
257      */
setExists(final boolean exists)258     public void setExists(final boolean exists) {
259         this.exists = exists;
260     }
261 
262     /**
263      * Sets the last modified time from the last time it was checked.
264      *
265      * @param lastModified The last modified time.
266      * @since 2.12.0
267      */
setLastModified(final FileTime lastModified)268     public void setLastModified(final FileTime lastModified) {
269         setLastModified(new SerializableFileTime(lastModified));
270     }
271 
272     /**
273      * Sets the last modified time from the last time it
274      * was checked.
275      *
276      * @param lastModified The last modified time in milliseconds.
277      */
setLastModified(final long lastModified)278     public void setLastModified(final long lastModified) {
279         setLastModified(FileTime.fromMillis(lastModified));
280     }
281 
setLastModified(final SerializableFileTime lastModified)282     void setLastModified(final SerializableFileTime lastModified) {
283         this.lastModified = lastModified;
284     }
285 
286     /**
287      * Sets the length.
288      *
289      * @param length the length
290      */
setLength(final long length)291     public void setLength(final long length) {
292         this.length = length;
293     }
294 
295     /**
296      * Sets the file name.
297      *
298      * @param name the file name
299      */
setName(final String name)300     public void setName(final String name) {
301         this.name = name;
302     }
303 }
304