• 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  * <h2>Deprecating Serialization</h2>
48  * <p>
49  * <em>Serialization is deprecated and will be removed in 3.0.</em>
50  * </p>
51  * @see FileAlterationObserver
52  * @since 2.0
53  */
54 public class FileEntry implements Serializable {
55 
56     private static final long serialVersionUID = -2505664948818681153L;
57 
58     static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {};
59 
60     private final FileEntry parent;
61     private FileEntry[] children;
62     private final File file;
63     private String name;
64     private boolean exists;
65     private boolean directory;
66     private SerializableFileTime lastModified = SerializableFileTime.EPOCH;
67     private long length;
68 
69     /**
70      * Constructs a new monitor for a specified {@link File}.
71      *
72      * @param file The file being monitored
73      */
FileEntry(final File file)74     public FileEntry(final File file) {
75         this(null, file);
76     }
77 
78     /**
79      * Constructs a new monitor for a specified {@link File}.
80      *
81      * @param parent The parent
82      * @param file The file being monitored
83      */
FileEntry(final FileEntry parent, final File file)84     public FileEntry(final FileEntry parent, final File file) {
85         this.file = Objects.requireNonNull(file, "file");
86         this.parent = parent;
87         this.name = file.getName();
88     }
89 
90     /**
91      * Gets the directory's files.
92      *
93      * @return This directory's files or an empty
94      * array if the file is not a directory or the
95      * directory is empty
96      */
getChildren()97     public FileEntry[] getChildren() {
98         return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
99     }
100 
101     /**
102      * Gets the file being monitored.
103      *
104      * @return the file being monitored
105      */
getFile()106     public File getFile() {
107         return file;
108     }
109 
110     /**
111      * Gets the last modified time from the last time it
112      * was checked.
113      *
114      * @return the last modified time in milliseconds.
115      */
getLastModified()116     public long getLastModified() {
117         return lastModified.toMillis();
118     }
119 
120     /**
121      * Gets the last modified time from the last time it was checked.
122      *
123      * @return the last modified time.
124      * @since 2.12.0
125      */
getLastModifiedFileTime()126     public FileTime getLastModifiedFileTime() {
127         return lastModified.unwrap();
128     }
129 
130     /**
131      * Gets the length.
132      *
133      * @return the length
134      */
getLength()135     public long getLength() {
136         return length;
137     }
138 
139     /**
140      * Gets the level
141      *
142      * @return the level
143      */
getLevel()144     public int getLevel() {
145         return parent == null ? 0 : parent.getLevel() + 1;
146     }
147 
148     /**
149      * Gets the file name.
150      *
151      * @return the file name
152      */
getName()153     public String getName() {
154         return name;
155     }
156 
157     /**
158      * Gets the parent entry.
159      *
160      * @return the parent entry
161      */
getParent()162     public FileEntry getParent() {
163         return parent;
164     }
165 
166     /**
167      * Tests whether the file is a directory or not.
168      *
169      * @return whether the file is a directory or not
170      */
isDirectory()171     public boolean isDirectory() {
172         return directory;
173     }
174 
175     /**
176      * Tests whether the file existed the last time it
177      * was checked.
178      *
179      * @return whether the file existed
180      */
isExists()181     public boolean isExists() {
182         return exists;
183     }
184 
185     /**
186      * Constructs a new child instance.
187      * <p>
188      * Custom implementations should override this method to return
189      * a new instance of the appropriate type.
190      * </p>
191      *
192      * @param file The child file
193      * @return a new child instance
194      */
newChildInstance(final File file)195     public FileEntry newChildInstance(final File file) {
196         return new FileEntry(this, file);
197     }
198 
199     /**
200      * Refreshes the attributes from the {@link File}, indicating
201      * whether the file has changed.
202      * <p>
203      * This implementation refreshes the {@code name}, {@code exists},
204      * {@code directory}, {@code lastModified} and {@code length}
205      * properties.
206      * </p>
207      * <p>
208      * The {@code exists}, {@code directory}, {@code lastModified}
209      * and {@code length} properties are compared for changes
210      * </p>
211      *
212      * @param file the file instance to compare to
213      * @return {@code true} if the file has changed, otherwise {@code false}
214      */
refresh(final File file)215     public boolean refresh(final File file) {
216         // cache original values
217         final boolean origExists = exists;
218         final SerializableFileTime origLastModified = lastModified;
219         final boolean origDirectory = directory;
220         final long origLength = length;
221 
222         // refresh the values
223         name = file.getName();
224         exists = Files.exists(file.toPath());
225         directory = exists && file.isDirectory();
226         try {
227             setLastModified(exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH);
228         } catch (final IOException e) {
229             setLastModified(SerializableFileTime.EPOCH);
230         }
231         length = exists && !directory ? file.length() : 0;
232 
233         // Return if there are changes
234         return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory
235             || length != origLength;
236     }
237 
238     /**
239      * Sets the directory's files.
240      *
241      * @param children This directory's files, may be null
242      */
setChildren(final FileEntry... children)243     public void setChildren(final FileEntry... children) {
244         this.children = children;
245     }
246 
247     /**
248      * Sets whether the file is a directory or not.
249      *
250      * @param directory whether the file is a directory or not
251      */
setDirectory(final boolean directory)252     public void setDirectory(final boolean directory) {
253         this.directory = directory;
254     }
255 
256     /**
257      * Sets whether the file existed the last time it
258      * was checked.
259      *
260      * @param exists whether the file exists or not
261      */
setExists(final boolean exists)262     public void setExists(final boolean exists) {
263         this.exists = exists;
264     }
265 
266     /**
267      * Sets the last modified time from the last time it was checked.
268      *
269      * @param lastModified The last modified time.
270      * @since 2.12.0
271      */
setLastModified(final FileTime lastModified)272     public void setLastModified(final FileTime lastModified) {
273         setLastModified(new SerializableFileTime(lastModified));
274     }
275 
276     /**
277      * Sets the last modified time from the last time it
278      * was checked.
279      *
280      * @param lastModified The last modified time in milliseconds.
281      */
setLastModified(final long lastModified)282     public void setLastModified(final long lastModified) {
283         setLastModified(FileTime.fromMillis(lastModified));
284     }
285 
setLastModified(final SerializableFileTime lastModified)286     void setLastModified(final SerializableFileTime lastModified) {
287         this.lastModified = lastModified;
288     }
289 
290     /**
291      * Sets the length.
292      *
293      * @param length the length
294      */
setLength(final long length)295     public void setLength(final long length) {
296         this.length = length;
297     }
298 
299     /**
300      * Sets the file name.
301      *
302      * @param name the file name
303      */
setName(final String name)304     public void setName(final String name) {
305         this.name = name;
306     }
307 }
308