001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.io.monitor;
018
019import java.io.File;
020import java.io.Serializable;
021
022/**
023 * {@link FileEntry} represents the state of a file or directory, capturing
024 * the following {@link File} attributes at a point in time.
025 * <ul>
026 *   <li>File Name (see {@link File#getName()})</li>
027 *   <li>Exists - whether the file exists or not (see {@link File#exists()})</li>
028 *   <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li>
029 *   <li>Last Modified Date/Time (see {@link File#lastModified()})</li>
030 *   <li>Length (see {@link File#length()}) - directories treated as zero</li>
031 *   <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li>
032 * </ul>
033 * <p>
034 * <h3>Custom Implementations</h3>
035 * If the state of additional {@link File} attributes is required then create a custom
036 * {@link FileEntry} with properties for those attributes. Override the
037 * {@link #newChildInstance(File)} to return a new instance of the appropriate type.
038 * You may also want to override the {@link #refresh(File)} method.
039 * @see FileAlterationObserver
040 * @since 2.0
041 */
042public class FileEntry implements Serializable {
043
044    static final FileEntry[] EMPTY_ENTRIES = new FileEntry[0];
045
046    private final FileEntry parent;
047    private FileEntry[] children;
048    private final File file;
049    private String name;
050    private boolean exists;
051    private boolean directory;
052    private long lastModified;
053    private long length;
054
055    /**
056     * Construct a new monitor for a specified {@link File}.
057     *
058     * @param file The file being monitored
059     */
060    public FileEntry(File file) {
061        this((FileEntry)null, file);
062    }
063
064    /**
065     * Construct a new monitor for a specified {@link File}.
066     *
067     * @param parent The parent
068     * @param file The file being monitored
069     */
070    public FileEntry(FileEntry parent, File file) {
071        if (file == null) {
072            throw new IllegalArgumentException("File is missing");
073        }
074        this.file = file;
075        this.parent = parent;
076        this.name = file.getName();
077    }
078
079    /**
080     * Refresh the attributes from the {@link File}, indicating
081     * whether the file has changed.
082     * <p>
083     * This implementation refreshes the <code>name</code>, <code>exists</code>,
084     * <code>directory</code>, <code>lastModified</code> and <code>length</code>
085     * properties.
086     * <p>
087     * The <code>exists</code>, <code>directory</code>, <code>lastModified</code>
088     * and <code>length</code> properties are compared for changes
089     *
090     * @param file the file instance to compare to
091     * @return {@code true} if the file has changed, otherwise {@code false}
092     */
093    public boolean refresh(File file) {
094
095        // cache original values
096        boolean origExists       = exists;
097        long    origLastModified = lastModified;
098        boolean origDirectory    = directory;
099        long    origLength       = length;
100
101        // refresh the values
102        name         = file.getName();
103        exists       = file.exists();
104        directory    = exists ? file.isDirectory() : false;
105        lastModified = exists ? file.lastModified() : 0;
106        length       = exists && !directory ? file.length() : 0;
107
108        // Return if there are changes
109        return exists != origExists ||
110                lastModified != origLastModified ||
111                directory != origDirectory ||
112                length != origLength;
113    }
114
115    /**
116     * Create a new child instance.
117     * <p>
118     * Custom implementations should override this method to return
119     * a new instance of the appropriate type.
120     *
121     * @param file The child file
122     * @return a new child instance
123     */
124    public FileEntry newChildInstance(File file) {
125        return new FileEntry(this, file);
126    }
127
128    /**
129     * Return the parent entry.
130     *
131     * @return the parent entry
132     */
133    public FileEntry getParent() {
134        return parent;
135    }
136
137    /**
138     * Return the level
139     *
140     * @return the level
141     */
142    public int getLevel() {
143        return parent == null ? 0 : parent.getLevel() + 1;
144    }
145
146    /**
147     * Return the directory's files.
148     *
149     * @return This directory's files or an empty
150     * array if the file is not a directory or the
151     * directory is empty
152     */
153    public FileEntry[] getChildren() {
154        return children != null ? children : EMPTY_ENTRIES;
155    }
156
157    /**
158     * Set the directory's files.
159     *
160     * @param children This directory's files, may be null
161     */
162    public void setChildren(FileEntry[] children) {
163        this.children = children;
164    }
165
166    /**
167     * Return the file being monitored.
168     *
169     * @return the file being monitored
170     */
171    public File getFile() {
172        return file;
173    }
174
175    /**
176     * Return the file name.
177     *
178     * @return the file name
179     */
180    public String getName() {
181        return name;
182    }
183
184    /**
185     * Set the file name.
186     *
187     * @param name the file name
188     */
189    public void setName(String name) {
190        this.name = name;
191    }
192
193    /**
194     * Return the last modified time from the last time it
195     * was checked.
196     *
197     * @return the last modified time
198     */
199    public long getLastModified() {
200        return lastModified;
201    }
202
203    /**
204     * Return the last modified time from the last time it
205     * was checked.
206     *
207     * @param lastModified The last modified time
208     */
209    public void setLastModified(long lastModified) {
210        this.lastModified = lastModified;
211    }
212
213    /**
214     * Return the length.
215     *
216     * @return the length
217     */
218    public long getLength() {
219        return length;
220    }
221
222    /**
223     * Set the length.
224     *
225     * @param length the length
226     */
227    public void setLength(long length) {
228        this.length = length;
229    }
230
231    /**
232     * Indicate whether the file existed the last time it
233     * was checked.
234     *
235     * @return whether the file existed
236     */
237    public boolean isExists() {
238        return exists;
239    }
240
241    /**
242     * Set whether the file existed the last time it
243     * was checked.
244     *
245     * @param exists whether the file exists or not
246     */
247    public void setExists(boolean exists) {
248        this.exists = exists;
249    }
250
251    /**
252     * Indicate whether the file is a directory or not.
253     *
254     * @return whether the file is a directory or not
255     */
256    public boolean isDirectory() {
257        return directory;
258    }
259
260    /**
261     * Set whether the file is a directory or not.
262     *
263     * @param directory whether the file is a directory or not
264     */
265    public void setDirectory(boolean directory) {
266        this.directory = directory;
267    }
268}