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.IOException; 021import java.io.Serializable; 022import java.nio.file.Files; 023 024import org.apache.commons.io.FileUtils; 025 026/** 027 * The state of a file or directory, capturing the following {@link File} attributes at a point in time. 028 * <ul> 029 * <li>File Name (see {@link File#getName()})</li> 030 * <li>Exists - whether the file exists or not (see {@link File#exists()})</li> 031 * <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li> 032 * <li>Last Modified Date/Time (see {@link FileUtils#lastModifiedUnchecked(File)})</li> 033 * <li>Length (see {@link File#length()}) - directories treated as zero</li> 034 * <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li> 035 * </ul> 036 * 037 * <h2>Custom Implementations</h2> 038 * <p> 039 * If the state of additional {@link File} attributes is required then create a custom 040 * {@link FileEntry} with properties for those attributes. Override the 041 * {@link #newChildInstance(File)} to return a new instance of the appropriate type. 042 * You may also want to override the {@link #refresh(File)} method. 043 * </p> 044 * @see FileAlterationObserver 045 * @since 2.0 046 */ 047public class FileEntry implements Serializable { 048 049 private static final long serialVersionUID = -2505664948818681153L; 050 051 static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {}; 052 053 private final FileEntry parent; 054 private FileEntry[] children; 055 private final File file; 056 private String name; 057 private boolean exists; 058 private boolean directory; 059 private long lastModified; 060 private long length; 061 062 /** 063 * Construct a new monitor for a specified {@link File}. 064 * 065 * @param file The file being monitored 066 */ 067 public FileEntry(final File file) { 068 this(null, file); 069 } 070 071 /** 072 * Construct a new monitor for a specified {@link File}. 073 * 074 * @param parent The parent 075 * @param file The file being monitored 076 */ 077 public FileEntry(final FileEntry parent, final File file) { 078 if (file == null) { 079 throw new IllegalArgumentException("File is missing"); 080 } 081 this.file = file; 082 this.parent = parent; 083 this.name = file.getName(); 084 } 085 086 /** 087 * Refresh the attributes from the {@link File}, indicating 088 * whether the file has changed. 089 * <p> 090 * This implementation refreshes the {@code name}, {@code exists}, 091 * {@code directory}, {@code lastModified} and {@code length} 092 * properties. 093 * <p> 094 * The {@code exists}, {@code directory}, {@code lastModified} 095 * and {@code length} properties are compared for changes 096 * 097 * @param file the file instance to compare to 098 * @return {@code true} if the file has changed, otherwise {@code false} 099 */ 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}