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.filefilter;
018
019import java.io.File;
020import java.io.Serializable;
021import java.nio.file.FileVisitResult;
022import java.nio.file.Path;
023import java.nio.file.attribute.BasicFileAttributes;
024import java.time.Instant;
025import java.util.Date;
026
027import org.apache.commons.io.FileUtils;
028import org.apache.commons.io.file.PathUtils;
029
030/**
031 * Filters files based on a cutoff time, can filter either newer files or files equal to or older.
032 * <p>
033 * For example, to print all files and directories in the current directory older than one day:
034 * </p>
035 * <h2>Using Classic IO</h2>
036 * <pre>
037 * Path dir = PathUtils.current();
038 * // We are interested in files older than one day
039 * Instant cutoff = Instant.now().minus(Duration.ofDays(1));
040 * String[] files = dir.list(new AgeFileFilter(cutoff));
041 * for (String file : files) {
042 *     System.out.println(file);
043 * }
044 * </pre>
045 *
046 * <h2>Using NIO</h2>
047 * <pre>
048 * Path dir = PathUtils.current();
049 * // We are interested in files older than one day
050 * Instant cutoff = Instant.now().minus(Duration.ofDays(1));
051 * AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new AgeFileFilter(cutoff));
052 * //
053 * // Walk one dir
054 * Files.<b>walkFileTree</b>(dir, Collections.emptySet(), 1, visitor);
055 * System.out.println(visitor.getPathCounters());
056 * System.out.println(visitor.getFileList());
057 * //
058 * visitor.getPathCounters().reset();
059 * //
060 * // Walk dir tree
061 * Files.<b>walkFileTree</b>(dir, visitor);
062 * System.out.println(visitor.getPathCounters());
063 * System.out.println(visitor.getDirList());
064 * System.out.println(visitor.getFileList());
065 * </pre>
066 * <h2>Deprecating Serialization</h2>
067 * <p>
068 * <em>Serialization is deprecated and will be removed in 3.0.</em>
069 * </p>
070 *
071 * @see FileFilterUtils#ageFileFilter(Date)
072 * @see FileFilterUtils#ageFileFilter(File)
073 * @see FileFilterUtils#ageFileFilter(long)
074 * @see FileFilterUtils#ageFileFilter(Date, boolean)
075 * @see FileFilterUtils#ageFileFilter(File, boolean)
076 * @see FileFilterUtils#ageFileFilter(long, boolean)
077 * @since 1.2
078 */
079public class AgeFileFilter extends AbstractFileFilter implements Serializable {
080
081    private static final long serialVersionUID = -2132740084016138541L;
082
083    /** Whether the files accepted will be older or newer. */
084    private final boolean acceptOlder;
085
086    /** The cutoff time threshold measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970). */
087    private final Instant cutoffInstant;
088
089    /**
090     * Constructs a new age file filter for files older than (at or before) a certain cutoff date.
091     *
092     * @param cutoffDate the threshold age of the files
093     */
094    public AgeFileFilter(final Date cutoffDate) {
095        this(cutoffDate, true);
096    }
097
098    /**
099     * Constructs a new age file filter for files on any one side of a certain cutoff date.
100     *
101     * @param cutoffDate the threshold age of the files
102     * @param acceptOlder if true, older files (at or before the cutoff) are accepted, else newer ones (after the
103     *        cutoff).
104     */
105    public AgeFileFilter(final Date cutoffDate, final boolean acceptOlder) {
106        this(cutoffDate.toInstant(), acceptOlder);
107    }
108
109    /**
110     * Constructs a new age file filter for files older than (at or before) a certain File (whose last modification time
111     * will be used as reference).
112     *
113     * @param cutoffReference the file whose last modification time is used as the threshold age of the files
114     */
115    public AgeFileFilter(final File cutoffReference) {
116        this(cutoffReference, true);
117    }
118
119    /**
120     * Constructs a new age file filter for files on any one side of a certain File (whose last modification time will
121     * be used as reference).
122     *
123     * @param cutoffReference the file whose last modification time is used as the threshold age of the files
124     * @param acceptOlder if true, older files (at or before the cutoff) are accepted, else newer ones (after the
125     *        cutoff).
126     */
127    public AgeFileFilter(final File cutoffReference, final boolean acceptOlder) {
128        this(FileUtils.lastModifiedUnchecked(cutoffReference), acceptOlder);
129    }
130
131    /**
132     * Constructs a new age file filter for files equal to or older than a certain cutoff.
133     *
134     * @param cutoffInstant The cutoff time threshold since the epoch (00:00:00 GMT, January 1, 1970).
135     * @since 2.12.0
136     */
137    public AgeFileFilter(final Instant cutoffInstant) {
138        this(cutoffInstant, true);
139    }
140
141    /**
142     * Constructs a new age file filter for files on any one side of a certain cutoff.
143     *
144     * @param cutoffInstant The cutoff time threshold since the epoch (00:00:00 GMT, January 1, 1970).
145     * @param acceptOlder if true, older files (at or before the cutoff) are accepted, else newer ones (after the cutoff).
146     * @since 2.12.0
147     */
148    public AgeFileFilter(final Instant cutoffInstant, final boolean acceptOlder) {
149        this.acceptOlder = acceptOlder;
150        this.cutoffInstant = cutoffInstant;
151    }
152
153    /**
154     * Constructs a new age file filter for files equal to or older than a certain cutoff
155     *
156     * @param cutoffMillis The cutoff time threshold measured in milliseconds since the epoch (00:00:00 GMT, January 1,
157     *        1970).
158     */
159    public AgeFileFilter(final long cutoffMillis) {
160        this(Instant.ofEpochMilli(cutoffMillis), true);
161    }
162
163    /**
164     * Constructs a new age file filter for files on any one side of a certain cutoff.
165     *
166     * @param cutoffMillis The cutoff time threshold measured in milliseconds since the epoch (00:00:00 GMT, January 1,
167     *        1970).
168     * @param acceptOlder if true, older files (at or before the cutoff) are accepted, else newer ones (after the
169     *        cutoff).
170     */
171    public AgeFileFilter(final long cutoffMillis, final boolean acceptOlder) {
172        this(Instant.ofEpochMilli(cutoffMillis), acceptOlder);
173    }
174
175    /**
176     * Checks to see if the last modification of the file matches cutoff favorably.
177     * <p>
178     * If last modification time equals cutoff and newer files are required, file <b>IS NOT</b> selected. If last
179     * modification time equals cutoff and older files are required, file <b>IS</b> selected.
180     * </p>
181     *
182     * @param file the File to check
183     * @return true if the file name matches
184     */
185    @Override
186    public boolean accept(final File file) {
187        return acceptOlder != FileUtils.isFileNewer(file, cutoffInstant);
188    }
189
190    /**
191     * Checks to see if the last modification of the file matches cutoff favorably.
192     * <p>
193     * If last modification time equals cutoff and newer files are required, file <b>IS NOT</b> selected. If last
194     * modification time equals cutoff and older files are required, file <b>IS</b> selected.
195     * </p>
196     * @param file the File to check
197     *
198     * @return true if the file name matches
199     * @since 2.9.0
200     */
201    @Override
202    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
203        return get(() -> toFileVisitResult(acceptOlder != PathUtils.isNewer(file, cutoffInstant)));
204    }
205
206    /**
207     * Provide a String representation of this file filter.
208     *
209     * @return a String representation
210     */
211    @Override
212    public String toString() {
213        final String condition = acceptOlder ? "<=" : ">";
214        return super.toString() + "(" + condition + cutoffInstant + ")";
215    }
216}