View Javadoc
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       */
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       */
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       */
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      */
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      */
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      */
126     public FileTime getLastModifiedFileTime() {
127         return lastModified.unwrap();
128     }
129 
130     /**
131      * Gets the length.
132      *
133      * @return the length
134      */
135     public long getLength() {
136         return length;
137     }
138 
139     /**
140      * Gets the level
141      *
142      * @return the level
143      */
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      */
153     public String getName() {
154         return name;
155     }
156 
157     /**
158      * Gets the parent entry.
159      *
160      * @return the parent entry
161      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
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      */
282     public void setLastModified(final long lastModified) {
283         setLastModified(FileTime.fromMillis(lastModified));
284     }
285 
286     void setLastModified(final SerializableFileTime lastModified) {
287         this.lastModified = lastModified;
288     }
289 
290     /**
291      * Sets the length.
292      *
293      * @param length the length
294      */
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      */
304     public void setName(final String name) {
305         this.name = name;
306     }
307 }