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.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     * Checks 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     * Checks 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    @Override
140    public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
141        return FileVisitResult.CONTINUE;
142    }
143
144    @Override
145    public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
146        return accept(dir, attributes);
147    }
148
149    /**
150     * Converts a boolean into a FileVisitResult.
151     *
152     * @param accept accepted or rejected.
153     * @return a FileVisitResult.
154     */
155    FileVisitResult toFileVisitResult(final boolean accept) {
156        return accept ? onAccept : onReject;
157    }
158
159    /**
160     * Provides a String representation of this file filter.
161     *
162     * @return a String representation
163     */
164    @Override
165    public String toString() {
166        return getClass().getSimpleName();
167    }
168
169    @Override
170    public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
171        return accept(file, attributes);
172    }
173
174    @Override
175    public FileVisitResult visitFileFailed(final Path file, final IOException exc) throws IOException {
176        return FileVisitResult.CONTINUE;
177    }
178
179}