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  
24  import org.apache.commons.io.FileUtils;
25  
26  /**
27   * The state of a file or directory, capturing the following {@link File} attributes at a point in time.
28   * <ul>
29   *   <li>File Name (see {@link File#getName()})</li>
30   *   <li>Exists - whether the file exists or not (see {@link File#exists()})</li>
31   *   <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li>
32   *   <li>Last Modified Date/Time (see {@link FileUtils#lastModifiedUnchecked(File)})</li>
33   *   <li>Length (see {@link File#length()}) - directories treated as zero</li>
34   *   <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li>
35   * </ul>
36   *
37   * <h2>Custom Implementations</h2>
38   * <p>
39   * If the state of additional {@link File} attributes is required then create a custom
40   * {@link FileEntry} with properties for those attributes. Override the
41   * {@link #newChildInstance(File)} to return a new instance of the appropriate type.
42   * You may also want to override the {@link #refresh(File)} method.
43   * </p>
44   * @see FileAlterationObserver
45   * @since 2.0
46   */
47  public class FileEntry implements Serializable {
48  
49      private static final long serialVersionUID = -2505664948818681153L;
50  
51      static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {};
52  
53      private final FileEntry parent;
54      private FileEntry[] children;
55      private final File file;
56      private String name;
57      private boolean exists;
58      private boolean directory;
59      private long lastModified;
60      private long length;
61  
62      /**
63       * Construct a new monitor for a specified {@link File}.
64       *
65       * @param file The file being monitored
66       */
67      public FileEntry(final File file) {
68          this(null, file);
69      }
70  
71      /**
72       * Construct a new monitor for a specified {@link File}.
73       *
74       * @param parent The parent
75       * @param file The file being monitored
76       */
77      public FileEntry/monitor/FileEntry.html#FileEntry">FileEntry(final FileEntry parent, final File file) {
78          if (file == null) {
79              throw new IllegalArgumentException("File is missing");
80          }
81          this.file = file;
82          this.parent = parent;
83          this.name = file.getName();
84      }
85  
86      /**
87       * Refresh the attributes from the {@link File}, indicating
88       * whether the file has changed.
89       * <p>
90       * This implementation refreshes the {@code name}, {@code exists},
91       * {@code directory}, {@code lastModified} and {@code length}
92       * properties.
93       * <p>
94       * The {@code exists}, {@code directory}, {@code lastModified}
95       * and {@code length} properties are compared for changes
96       *
97       * @param file the file instance to compare to
98       * @return {@code true} if the file has changed, otherwise {@code false}
99       */
100     public boolean refresh(final File file) {
101         // cache original values
102         final boolean origExists = exists;
103         final long origLastModified = lastModified;
104         final boolean origDirectory = directory;
105         final long origLength = length;
106 
107         // refresh the values
108         name = file.getName();
109         exists = Files.exists(file.toPath());
110         directory = exists && file.isDirectory();
111         try {
112             lastModified = exists ? FileUtils.lastModified(file) : 0;
113         } catch (final IOException e) {
114             lastModified = 0;
115         }
116         length = exists && !directory ? file.length() : 0;
117 
118         // Return if there are changes
119         return exists != origExists || lastModified != origLastModified || directory != origDirectory
120             || length != origLength;
121     }
122 
123     /**
124      * Create a new child instance.
125      * <p>
126      * Custom implementations should override this method to return
127      * a new instance of the appropriate type.
128      *
129      * @param file The child file
130      * @return a new child instance
131      */
132     public FileEntry newChildInstance(final File file) {
133         return new FileEntry(this, file);
134     }
135 
136     /**
137      * Return the parent entry.
138      *
139      * @return the parent entry
140      */
141     public FileEntry getParent() {
142         return parent;
143     }
144 
145     /**
146      * Return the level
147      *
148      * @return the level
149      */
150     public int getLevel() {
151         return parent == null ? 0 : parent.getLevel() + 1;
152     }
153 
154     /**
155      * Return the directory's files.
156      *
157      * @return This directory's files or an empty
158      * array if the file is not a directory or the
159      * directory is empty
160      */
161     public FileEntry[] getChildren() {
162         return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
163     }
164 
165     /**
166      * Set the directory's files.
167      *
168      * @param children This directory's files, may be null
169      */
170     public void setChildren(final FileEntry... children) {
171         this.children = children;
172     }
173 
174     /**
175      * Return the file being monitored.
176      *
177      * @return the file being monitored
178      */
179     public File getFile() {
180         return file;
181     }
182 
183     /**
184      * Return the file name.
185      *
186      * @return the file name
187      */
188     public String getName() {
189         return name;
190     }
191 
192     /**
193      * Set the file name.
194      *
195      * @param name the file name
196      */
197     public void setName(final String name) {
198         this.name = name;
199     }
200 
201     /**
202      * Return the last modified time from the last time it
203      * was checked.
204      *
205      * @return the last modified time
206      */
207     public long getLastModified() {
208         return lastModified;
209     }
210 
211     /**
212      * Return the last modified time from the last time it
213      * was checked.
214      *
215      * @param lastModified The last modified time
216      */
217     public void setLastModified(final long lastModified) {
218         this.lastModified = lastModified;
219     }
220 
221     /**
222      * Return the length.
223      *
224      * @return the length
225      */
226     public long getLength() {
227         return length;
228     }
229 
230     /**
231      * Set the length.
232      *
233      * @param length the length
234      */
235     public void setLength(final long length) {
236         this.length = length;
237     }
238 
239     /**
240      * Indicate whether the file existed the last time it
241      * was checked.
242      *
243      * @return whether the file existed
244      */
245     public boolean isExists() {
246         return exists;
247     }
248 
249     /**
250      * Set whether the file existed the last time it
251      * was checked.
252      *
253      * @param exists whether the file exists or not
254      */
255     public void setExists(final boolean exists) {
256         this.exists = exists;
257     }
258 
259     /**
260      * Indicate whether the file is a directory or not.
261      *
262      * @return whether the file is a directory or not
263      */
264     public boolean isDirectory() {
265         return directory;
266     }
267 
268     /**
269      * Set whether the file is a directory or not.
270      *
271      * @param directory whether the file is a directory or not
272      */
273     public void setDirectory(final boolean directory) {
274         this.directory = directory;
275     }
276 }