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.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027import java.util.Objects;
028
029/**
030 * A {@link java.io.FileFilter} providing conditional AND logic across a list of
031 * file filters. This filter returns {@code true} if all filters in the
032 * list return {@code true}. Otherwise, it returns {@code false}.
033 * Checking of the file filter list stops when the first filter returns
034 * {@code false}.
035 *
036 * @since 1.0
037 * @see FileFilterUtils#and(IOFileFilter...)
038 */
039public class AndFileFilter
040        extends AbstractFileFilter
041        implements ConditionalFileFilter, Serializable {
042
043    private static final long serialVersionUID = 7215974688563965257L;
044
045    /** The list of file filters. */
046    private final List<IOFileFilter> fileFilters;
047
048    /**
049     * Constructs a new empty instance.
050     *
051     * @since 1.1
052     */
053    public AndFileFilter() {
054        this(0);
055    }
056
057    /**
058     * Constructs a new instance with the given initial list.
059     *
060     * @param initialList the initial list.
061     */
062    private AndFileFilter(final ArrayList<IOFileFilter> initialList) {
063        this.fileFilters = Objects.requireNonNull(initialList, "initialList");
064    }
065
066    /**
067     * Constructs a new instance with the given initial capacity.
068     *
069     * @param initialCapacity the initial capacity.
070     */
071    private AndFileFilter(final int initialCapacity) {
072        this(new ArrayList<>(initialCapacity));
073    }
074
075    /**
076     * Constructs a new file filter that ANDs the result of other filters.
077     *
078     * @param filter1  the first filter, must second be null
079     * @param filter2  the first filter, must not be null
080     * @throws IllegalArgumentException if either filter is null
081     */
082    public AndFileFilter(final IOFileFilter filter1, final IOFileFilter filter2) {
083        this(2);
084        addFileFilter(filter1);
085        addFileFilter(filter2);
086    }
087
088    /**
089     * Constructs a new instance for the give filters.
090     * @param fileFilters filters to OR.
091     *
092     * @since 2.9.0
093     */
094    public AndFileFilter(final IOFileFilter... fileFilters) {
095        this(Objects.requireNonNull(fileFilters, "fileFilters").length);
096        addFileFilter(fileFilters);
097    }
098
099    /**
100     * Constructs a new instance of {@code AndFileFilter}
101     * with the specified list of filters.
102     *
103     * @param fileFilters  a List of IOFileFilter instances, copied.
104     * @since 1.1
105     */
106    public AndFileFilter(final List<IOFileFilter> fileFilters) {
107        this(new ArrayList<>(Objects.requireNonNull(fileFilters, "fileFilters")));
108    }
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public boolean accept(final File file) {
115        if (isEmpty()) {
116            return false;
117        }
118        for (final IOFileFilter fileFilter : fileFilters) {
119            if (!fileFilter.accept(file)) {
120                return false;
121            }
122        }
123        return true;
124    }
125
126    /**
127     * {@inheritDoc}
128     */
129    @Override
130    public boolean accept(final File file, final String name) {
131        if (isEmpty()) {
132            return false;
133        }
134        for (final IOFileFilter fileFilter : fileFilters) {
135            if (!fileFilter.accept(file, name)) {
136                return false;
137            }
138        }
139        return true;
140    }
141
142    /**
143     * {@inheritDoc}
144     * @since 2.9.0
145     */
146    @Override
147    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
148        if (isEmpty()) {
149            return FileVisitResult.TERMINATE;
150        }
151        for (final IOFileFilter fileFilter : fileFilters) {
152            if (fileFilter.accept(file, attributes) != FileVisitResult.CONTINUE) {
153                return FileVisitResult.TERMINATE;
154            }
155        }
156        return FileVisitResult.CONTINUE;
157    }
158
159    /**
160     * {@inheritDoc}
161     */
162    @Override
163    public void addFileFilter(final IOFileFilter fileFilter) {
164        this.fileFilters.add(Objects.requireNonNull(fileFilter, "fileFilter"));
165    }
166
167    /**
168     * Adds the given file filters.
169     *
170     * @param fileFilters the filters to add.
171     * @since 2.9.0
172     */
173    public void addFileFilter(final IOFileFilter... fileFilters) {
174        for (final IOFileFilter fileFilter : Objects.requireNonNull(fileFilters, "fileFilters")) {
175            addFileFilter(fileFilter);
176        }
177    }
178
179    /**
180     * {@inheritDoc}
181     */
182    @Override
183    public List<IOFileFilter> getFileFilters() {
184        return Collections.unmodifiableList(this.fileFilters);
185    }
186
187    private boolean isEmpty() {
188        return this.fileFilters.isEmpty();
189    }
190
191    /**
192     * {@inheritDoc}
193     */
194    @Override
195    public boolean removeFileFilter(final IOFileFilter ioFileFilter) {
196        return this.fileFilters.remove(ioFileFilter);
197    }
198
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    public void setFileFilters(final List<IOFileFilter> fileFilters) {
204        this.fileFilters.clear();
205        this.fileFilters.addAll(fileFilters);
206    }
207
208    /**
209     * Provide a String representation of this file filter.
210     *
211     * @return a String representation
212     */
213    @Override
214    public String toString() {
215        final StringBuilder buffer = new StringBuilder();
216        buffer.append(super.toString());
217        buffer.append("(");
218        for (int i = 0; i < fileFilters.size(); i++) {
219            if (i > 0) {
220                buffer.append(",");
221            }
222            buffer.append(fileFilters.get(i));
223        }
224        buffer.append(")");
225        return buffer.toString();
226    }
227
228}