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