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 *      https://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.FileFilter;
021import java.io.FilenameFilter;
022import java.io.IOException;
023import java.nio.file.FileVisitResult;
024import java.nio.file.Path;
025import java.nio.file.attribute.BasicFileAttributes;
026import java.util.List;
027import java.util.Objects;
028
029import org.apache.commons.io.file.PathFilter;
030import org.apache.commons.io.file.PathVisitor;
031import org.apache.commons.io.function.IOSupplier;
032
033/**
034 * Abstracts the implementation of the {@link FileFilter} (IO), {@link FilenameFilter} (IO), {@link PathFilter} (NIO)
035 * interfaces via our own {@link IOFileFilter} interface.
036 * <p>
037 * Note that a subclass MUST override one of the {@code accept} methods, otherwise that subclass will infinitely loop.
038 * </p>
039 *
040 * @since 1.0
041 */
042public abstract class AbstractFileFilter implements IOFileFilter, PathVisitor {
043
044    static FileVisitResult toDefaultFileVisitResult(final boolean accept) {
045        return accept ? FileVisitResult.CONTINUE : FileVisitResult.TERMINATE;
046    }
047
048    /**
049     * What to do when this filter accepts.
050     */
051    private final FileVisitResult onAccept;
052
053    /**
054     * What to do when this filter rejects.
055     */
056    private final FileVisitResult onReject;
057
058    /**
059     * Constructs a new instance.
060     */
061    public AbstractFileFilter() {
062        this(FileVisitResult.CONTINUE, FileVisitResult.TERMINATE);
063    }
064
065    /**
066     * Constructs a new instance.
067     *
068     * @param onAccept What to do on acceptance.
069     * @param onReject What to do on rejection.
070     * @since 2.12.0.
071     */
072    protected AbstractFileFilter(final FileVisitResult onAccept, final FileVisitResult onReject) {
073        this.onAccept = onAccept;
074        this.onReject = onReject;
075    }
076
077    /**
078     * Tests to see if the File should be accepted by this filter.
079     *
080     * @param file the File to check
081     * @return true if this file matches the test
082     */
083    @Override
084    public boolean accept(final File file) {
085        Objects.requireNonNull(file, "file");
086        return accept(file.getParentFile(), file.getName());
087    }
088
089    /**
090     * Tests to see if the File should be accepted by this filter.
091     *
092     * @param dir the directory File to check
093     * @param name the file name within the directory to check
094     * @return true if this file matches the test
095     */
096    @Override
097    public boolean accept(final File dir, final String name) {
098        Objects.requireNonNull(name, "name");
099        return accept(new File(dir, name));
100    }
101
102    void append(final List<?> list, final StringBuilder buffer) {
103        for (int i = 0; i < list.size(); i++) {
104            if (i > 0) {
105                buffer.append(",");
106            }
107            buffer.append(list.get(i));
108        }
109    }
110
111    void append(final Object[] array, final StringBuilder buffer) {
112        for (int i = 0; i < array.length; i++) {
113            if (i > 0) {
114                buffer.append(",");
115            }
116            buffer.append(array[i]);
117        }
118    }
119
120    FileVisitResult get(final IOSupplier<FileVisitResult> supplier) {
121        try {
122            return supplier.get();
123        } catch (final IOException e) {
124            return handle(e);
125        }
126    }
127
128    /**
129     * Handles exceptions caught while accepting.
130     *
131     * @param t the caught Throwable.
132     * @return the given Throwable.
133     * @since 2.9.0
134     */
135    protected FileVisitResult handle(final Throwable t) {
136        return FileVisitResult.TERMINATE;
137    }
138
139    boolean isDirectory(final File file) {
140        return file != null && file.isDirectory();
141    }
142
143    boolean isFile(final File file) {
144        return file != null && file.isFile();
145    }
146
147    @Override
148    public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
149        return FileVisitResult.CONTINUE;
150    }
151
152    @Override
153    public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
154        return accept(dir, attributes);
155    }
156
157    /**
158     * Converts a boolean into a FileVisitResult.
159     *
160     * @param accept accepted or rejected.
161     * @return a FileVisitResult.
162     */
163    FileVisitResult toFileVisitResult(final boolean accept) {
164        return accept ? onAccept : onReject;
165    }
166
167    /**
168     * Provides a String representation of this file filter.
169     *
170     * @return a String representation
171     */
172    @Override
173    public String toString() {
174        return getClass().getSimpleName();
175    }
176
177    @Override
178    public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
179        return accept(file, attributes);
180    }
181
182    @Override
183    public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException {
184        return FileVisitResult.CONTINUE;
185    }
186
187}