FileEntry.java

  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.  *      https://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. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.Serializable;
  21. import java.nio.file.Files;
  22. import java.nio.file.attribute.FileTime;
  23. import java.util.Objects;

  24. import org.apache.commons.io.FileUtils;
  25. import org.apache.commons.io.file.attribute.FileTimes;

  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.  * <h2>Deprecating Serialization</h2>
  45.  * <p>
  46.  * <em>Serialization is deprecated and will be removed in 3.0.</em>
  47.  * </p>
  48.  * @see FileAlterationObserver
  49.  * @since 2.0
  50.  */
  51. public class FileEntry implements Serializable {

  52.     private static final long serialVersionUID = -2505664948818681153L;

  53.     static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {};

  54.     /** The parent. */
  55.     private final FileEntry parent;

  56.     /** My children. */
  57.     private FileEntry[] children;

  58.     /** Monitored file. */
  59.     private final File file;

  60.     /** Monitored file name. */
  61.     private String name;

  62.     /** Whether the file exists. */
  63.     private boolean exists;

  64.     /** Whether the file is a directory or not. */
  65.     private boolean directory;

  66.     /** The file's last modified timestamp. */
  67.     private SerializableFileTime lastModified = SerializableFileTime.EPOCH;

  68.     /** The file's length. */
  69.     private long length;

  70.     /**
  71.      * Constructs a new monitor for a specified {@link File}.
  72.      *
  73.      * @param file The file being monitored
  74.      */
  75.     public FileEntry(final File file) {
  76.         this(null, file);
  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.      * Gets the directory's files.
  91.      *
  92.      * @return This directory's files or an empty
  93.      * array if the file is not a directory or the
  94.      * directory is empty
  95.      */
  96.     public FileEntry[] getChildren() {
  97.         return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
  98.     }

  99.     /**
  100.      * Gets the file being monitored.
  101.      *
  102.      * @return the file being monitored
  103.      */
  104.     public File getFile() {
  105.         return file;
  106.     }

  107.     /**
  108.      * Gets the last modified time from the last time it
  109.      * was checked.
  110.      *
  111.      * @return the last modified time in milliseconds.
  112.      */
  113.     public long getLastModified() {
  114.         return lastModified.toMillis();
  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.      */
  122.     public FileTime getLastModifiedFileTime() {
  123.         return lastModified.unwrap();
  124.     }

  125.     /**
  126.      * Gets the length.
  127.      *
  128.      * @return the length
  129.      */
  130.     public long getLength() {
  131.         return length;
  132.     }

  133.     /**
  134.      * Gets the level
  135.      *
  136.      * @return the level
  137.      */
  138.     public int getLevel() {
  139.         return parent == null ? 0 : parent.getLevel() + 1;
  140.     }

  141.     /**
  142.      * Gets the file name.
  143.      *
  144.      * @return the file name
  145.      */
  146.     public String getName() {
  147.         return name;
  148.     }

  149.     /**
  150.      * Gets the parent entry.
  151.      *
  152.      * @return the parent entry
  153.      */
  154.     public FileEntry getParent() {
  155.         return parent;
  156.     }

  157.     /**
  158.      * Tests whether the file is a directory or not.
  159.      *
  160.      * @return whether the file is a directory or not
  161.      */
  162.     public boolean isDirectory() {
  163.         return directory;
  164.     }

  165.     /**
  166.      * Tests whether the file existed the last time it
  167.      * was checked.
  168.      *
  169.      * @return whether the file existed
  170.      */
  171.     public boolean isExists() {
  172.         return exists;
  173.     }

  174.     /**
  175.      * Constructs a new child instance.
  176.      * <p>
  177.      * Custom implementations should override this method to return
  178.      * a new instance of the appropriate type.
  179.      * </p>
  180.      *
  181.      * @param file The child file
  182.      * @return a new child instance
  183.      */
  184.     public FileEntry newChildInstance(final File file) {
  185.         return new FileEntry(this, file);
  186.     }

  187.     /**
  188.      * Refreshes the attributes from the {@link File}, indicating
  189.      * whether the file has changed.
  190.      * <p>
  191.      * This implementation refreshes the {@code name}, {@code exists},
  192.      * {@code directory}, {@code lastModified} and {@code length}
  193.      * properties.
  194.      * </p>
  195.      * <p>
  196.      * The {@code exists}, {@code directory}, {@code lastModified}
  197.      * and {@code length} properties are compared for changes
  198.      * </p>
  199.      *
  200.      * @param file the file instance to compare to
  201.      * @return {@code true} if the file has changed, otherwise {@code false}
  202.      */
  203.     public boolean refresh(final File file) {
  204.         // cache original values
  205.         final boolean origExists = exists;
  206.         final SerializableFileTime origLastModified = lastModified;
  207.         final boolean origDirectory = directory;
  208.         final long origLength = length;

  209.         // refresh the values
  210.         name = file.getName();
  211.         exists = Files.exists(file.toPath());
  212.         directory = exists && file.isDirectory();
  213.         try {
  214.             setLastModified(exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH);
  215.         } catch (final IOException e) {
  216.             setLastModified(SerializableFileTime.EPOCH);
  217.         }
  218.         length = exists && !directory ? file.length() : 0;

  219.         // Return if there are changes
  220.         return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory
  221.             || length != origLength;
  222.     }

  223.     /**
  224.      * Sets the directory's files.
  225.      *
  226.      * @param children This directory's files, may be null
  227.      */
  228.     public void setChildren(final FileEntry... children) {
  229.         this.children = children;
  230.     }

  231.     /**
  232.      * Sets whether the file is a directory or not.
  233.      *
  234.      * @param directory whether the file is a directory or not
  235.      */
  236.     public void setDirectory(final boolean directory) {
  237.         this.directory = directory;
  238.     }

  239.     /**
  240.      * Sets whether the file existed the last time it
  241.      * was checked.
  242.      *
  243.      * @param exists whether the file exists or not
  244.      */
  245.     public void setExists(final boolean exists) {
  246.         this.exists = exists;
  247.     }

  248.     /**
  249.      * Sets the last modified time from the last time it was checked.
  250.      *
  251.      * @param lastModified The last modified time.
  252.      * @since 2.12.0
  253.      */
  254.     public void setLastModified(final FileTime lastModified) {
  255.         setLastModified(new SerializableFileTime(lastModified));
  256.     }

  257.     /**
  258.      * Sets the last modified time from the last time it
  259.      * was checked.
  260.      *
  261.      * @param lastModified The last modified time in milliseconds.
  262.      */
  263.     public void setLastModified(final long lastModified) {
  264.         setLastModified(FileTime.fromMillis(lastModified));
  265.     }

  266.     void setLastModified(final SerializableFileTime lastModified) {
  267.         this.lastModified = lastModified;
  268.     }

  269.     /**
  270.      * Sets the length.
  271.      *
  272.      * @param length the length
  273.      */
  274.     public void setLength(final long length) {
  275.         this.length = length;
  276.     }

  277.     /**
  278.      * Sets the file name.
  279.      *
  280.      * @param name the file name
  281.      */
  282.     public void setName(final String name) {
  283.         this.name = name;
  284.     }
  285. }