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.vfs2.filter;
018
019import java.io.Serializable;
020import java.util.Date;
021import java.util.Objects;
022
023import org.apache.commons.vfs2.FileContent;
024import org.apache.commons.vfs2.FileFilter;
025import org.apache.commons.vfs2.FileObject;
026import org.apache.commons.vfs2.FileSelectInfo;
027import org.apache.commons.vfs2.FileSystemException;
028
029/**
030 * Filters files based on a cutoff time, can filter either newer files or files
031 * equal to or older.
032 * <p>
033 * For example, to print all files and directories in the current directory
034 * older than one day:
035 * </p>
036 *
037 * <pre>
038 * FileSystemManager fsManager = VFS.getManager();
039 * FileObject dir = fsManager.toFileObject(new File(&quot;.&quot;));
040 * // We are interested in files older than one day
041 * long cutoff = System.currentTimeMillis() - (24 * 60 * 60 * 1000);
042 * AgeFileFilter filter = new AgeFileFilter(cutoff);
043 * FileObject[] files = dir.findFiles(new FileFilterSelector(filter));
044 * for (int i = 0; i &lt; files.length; i++) {
045 *     System.out.println(files[i]);
046 * }
047 * </pre>
048 *
049 * @author This code was originally ported from Apache Commons IO File Filter
050 * @see "https://commons.apache.org/proper/commons-io/"
051 * @since 2.4
052 */
053public class AgeFileFilter implements FileFilter, Serializable {
054
055    private static final long serialVersionUID = 1L;
056
057    /**
058     * Tests if the specified {@code File} is newer than the specified time
059     * reference.
060     *
061     * @param fileObject the {@code File} of which the modification date must
062     *                   be compared, must not be {@code null}
063     * @param timeMillis the time reference measured in milliseconds since the epoch
064     *                   (00:00:00 GMT, January 1, 1970)
065     * @return true if the {@code File} exists and has been modified after the
066     *         given time reference.
067     * @throws FileSystemException Thrown for file system errors.
068     * @throws IllegalArgumentException if the file is {@code null}
069     */
070    private static boolean isFileNewer(final FileObject fileObject, final long timeMillis) throws FileSystemException {
071        Objects.requireNonNull(fileObject, "fileObject");
072        if (!fileObject.exists()) {
073            return false;
074        }
075        try (FileContent content = fileObject.getContent()) {
076            return content.getLastModifiedTime() > timeMillis;
077        }
078    }
079
080    /** Whether the files accepted will be older or newer. */
081    private final boolean acceptOlder;
082
083    /** The cutoff time threshold. */
084    private final long cutoff;
085
086    /**
087     * Constructs a new age file filter for files older than (at or before) a
088     * certain cutoff date.
089     *
090     * @param cutoffDate the threshold age of the files
091     */
092    public AgeFileFilter(final Date cutoffDate) {
093        this(cutoffDate, true);
094    }
095
096    /**
097     * Constructs a new age file filter for files on any one side of a certain
098     * cutoff date.
099     *
100     * @param cutoffDate  the threshold age of the files
101     * @param acceptOlder if true, older files (at or before the cutoff) are
102     *                    accepted, else newer ones (after the cutoff).
103     */
104    public AgeFileFilter(final Date cutoffDate, final boolean acceptOlder) {
105        this(cutoffDate.getTime(), acceptOlder);
106    }
107
108    /**
109     * Constructs a new age file filter for files older than (at or before) a
110     * certain File (whose last modification time will be used as reference).
111     *
112     * @param cutoffReference the file whose last modification time is used as the
113     *                        threshold age of the files
114     *
115     * @throws FileSystemException Error reading the last modification time from the
116     *                             reference file object.
117     */
118    public AgeFileFilter(final FileObject cutoffReference) throws FileSystemException {
119        this(cutoffReference, true);
120    }
121
122    /**
123     * Constructs a new age file filter for files on any one side of a certain File
124     * (whose last modification time will be used as reference).
125     *
126     * @param cutoffReference the file whose last modification time is used as the
127     *                        threshold age of the files
128     * @param acceptOlder     if true, older files (at or before the cutoff) are
129     *                        accepted, else newer ones (after the cutoff).
130     *
131     * @throws FileSystemException Error reading the last modification time from the
132     *                             reference file object.
133     */
134    public AgeFileFilter(final FileObject cutoffReference, final boolean acceptOlder) throws FileSystemException {
135        this(cutoffReference.getContent().getLastModifiedTime(), acceptOlder);
136    }
137
138    /**
139     * Constructs a new age file filter for files equal to or older than a certain
140     * cutoff.
141     *
142     * @param cutoff the threshold age of the files
143     */
144    public AgeFileFilter(final long cutoff) {
145        this(cutoff, true);
146    }
147
148    /**
149     * Constructs a new age file filter for files on any one side of a certain
150     * cutoff.
151     *
152     * @param cutoff      the threshold age of the files
153     * @param acceptOlder if true, older files (at or before the cutoff) are
154     *                    accepted, else newer ones (after the cutoff).
155     */
156    public AgeFileFilter(final long cutoff, final boolean acceptOlder) {
157        this.acceptOlder = acceptOlder;
158        this.cutoff = cutoff;
159    }
160
161    /**
162     * Checks to see if the last modification of the file matches cutoff favorably.
163     * <p>
164     * If last modification time equals cutoff and newer files are required, file
165     * <strong>IS NOT</strong> selected. If last modification time equals cutoff and older
166     * files are required, file <strong>IS</strong> selected.
167     * </p>
168     *
169     * @param fileInfo the File to check
170     * @return true if the file name matches
171     * @throws FileSystemException Thrown for file system errors.
172     */
173    @Override
174    public boolean accept(final FileSelectInfo fileInfo) throws FileSystemException {
175        return acceptOlder != isFileNewer(fileInfo.getFile(), cutoff);
176    }
177
178    /**
179     * Provide a String representation of this file filter.
180     *
181     * @return a String representation
182     */
183    @Override
184    public String toString() {
185        final String condition = acceptOlder ? "<=" : ">";
186        return super.toString() + "(" + condition + cutoff + ")";
187    }
188}