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.List;
025import java.util.Objects;
026import java.util.stream.Stream;
027
028import org.apache.commons.io.IOCase;
029
030/**
031 * Filters file names for a certain prefix.
032 * <p>
033 * For example, to print all files and directories in the
034 * current directory whose name starts with {@code Test}:
035 * </p>
036 * <h2>Using Classic IO</h2>
037 * <pre>
038 * File dir = FileUtils.current();
039 * String[] files = dir.list(new PrefixFileFilter("Test"));
040 * for (String file : files) {
041 *     System.out.println(file);
042 * }
043 * </pre>
044 *
045 * <h2>Using NIO</h2>
046 * <pre>
047 * final Path dir = PathUtils.current();
048 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new PrefixFileFilter("Test"));
049 * //
050 * // Walk one dir
051 * Files.<b>walkFileTree</b>(dir, Collections.emptySet(), 1, visitor);
052 * System.out.println(visitor.getPathCounters());
053 * System.out.println(visitor.getFileList());
054 * //
055 * visitor.getPathCounters().reset();
056 * //
057 * // Walk dir tree
058 * Files.<b>walkFileTree</b>(dir, visitor);
059 * System.out.println(visitor.getPathCounters());
060 * System.out.println(visitor.getDirList());
061 * System.out.println(visitor.getFileList());
062 * </pre>
063 * <h2>Deprecating Serialization</h2>
064 * <p>
065 * <em>Serialization is deprecated and will be removed in 3.0.</em>
066 * </p>
067 *
068 * @since 1.0
069 * @see FileFilterUtils#prefixFileFilter(String)
070 * @see FileFilterUtils#prefixFileFilter(String, IOCase)
071 */
072public class PrefixFileFilter extends AbstractFileFilter implements Serializable {
073
074    private static final long serialVersionUID = 8533897440809599867L;
075
076    /** The file name prefixes to search for */
077    private final String[] prefixes;
078
079    /** Whether the comparison is case-sensitive. */
080    private final IOCase isCase;
081
082    /**
083     * Constructs a new Prefix file filter for a list of prefixes.
084     *
085     * @param prefixes  the prefixes to allow, must not be null
086     * @throws NullPointerException if the prefix list is null
087     * @throws ClassCastException if the list does not contain Strings
088     */
089    public PrefixFileFilter(final List<String> prefixes) {
090        this(prefixes, IOCase.SENSITIVE);
091    }
092
093    /**
094     * Constructs a new Prefix file filter for a list of prefixes
095     * specifying case-sensitivity.
096     *
097     * @param prefixes  the prefixes to allow, must not be null
098     * @param ioCase  how to handle case sensitivity, null means case-sensitive
099     * @throws NullPointerException if the prefix list is null
100     * @throws ClassCastException if the list does not contain Strings
101     * @since 1.4
102     */
103    public PrefixFileFilter(final List<String> prefixes, final IOCase ioCase) {
104        Objects.requireNonNull(prefixes, "prefixes");
105        this.prefixes = prefixes.toArray(EMPTY_STRING_ARRAY);
106        this.isCase = IOCase.value(ioCase, IOCase.SENSITIVE);
107    }
108
109    /**
110     * Constructs a new Prefix file filter for a single prefix.
111     *
112     * @param prefix  the prefix to allow, must not be null
113     * @throws IllegalArgumentException if the prefix is null
114     */
115    public PrefixFileFilter(final String prefix) {
116        this(prefix, IOCase.SENSITIVE);
117    }
118
119    /**
120     * Constructs a new Prefix file filter for any of an array of prefixes.
121     * <p>
122     * The array is not cloned, so could be changed after constructing the
123     * instance. This would be inadvisable however.
124     *
125     * @param prefixes  the prefixes to allow, must not be null
126     * @throws IllegalArgumentException if the prefix array is null
127     */
128    public PrefixFileFilter(final String... prefixes) {
129        this(prefixes, IOCase.SENSITIVE);
130    }
131
132    /**
133     * Constructs a new Prefix file filter for a single prefix
134     * specifying case-sensitivity.
135     *
136     * @param prefix  the prefix to allow, must not be null
137     * @param ioCase  how to handle case sensitivity, null means case-sensitive
138     * @throws IllegalArgumentException if the prefix is null
139     * @since 1.4
140     */
141    public PrefixFileFilter(final String prefix, final IOCase ioCase) {
142        Objects.requireNonNull(prefix, "prefix");
143        this.prefixes = new String[] {prefix};
144        this.isCase = IOCase.value(ioCase, IOCase.SENSITIVE);
145    }
146
147    /**
148     * Constructs a new Prefix file filter for any of an array of prefixes
149     * specifying case-sensitivity.
150     *
151     * @param prefixes  the prefixes to allow, must not be null
152     * @param ioCase  how to handle case sensitivity, null means case-sensitive
153     * @throws IllegalArgumentException if the prefix is null
154     * @since 1.4
155     */
156    public PrefixFileFilter(final String[] prefixes, final IOCase ioCase) {
157        Objects.requireNonNull(prefixes, "prefixes");
158        this.prefixes = prefixes.clone();
159        this.isCase = IOCase.value(ioCase, IOCase.SENSITIVE);
160    }
161
162    /**
163     * Checks to see if the file name starts with the prefix.
164     *
165     * @param file  the File to check
166     * @return true if the file name starts with one of our prefixes
167     */
168    @Override
169    public boolean accept(final File file) {
170        return accept(file == null ? null : file.getName());
171    }
172
173    /**
174     * Checks to see if the file name starts with the prefix.
175     *
176     * @param file  the File directory
177     * @param name  the file name
178     * @return true if the file name starts with one of our prefixes
179     */
180    @Override
181    public boolean accept(final File file, final String name) {
182        return accept(name);
183    }
184
185    /**
186     * Checks to see if the file name starts with the prefix.
187     * @param file  the File to check
188     *
189     * @return true if the file name starts with one of our prefixes
190     * @since 2.9.0
191     */
192    @Override
193    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
194        final Path fileName = file.getFileName();
195        return toFileVisitResult(accept(fileName == null ? null : fileName.toFile()));
196    }
197
198    private boolean accept(final String name) {
199        return Stream.of(prefixes).anyMatch(prefix -> isCase.checkStartsWith(name, prefix));
200    }
201
202    /**
203     * Provide a String representation of this file filter.
204     *
205     * @return a String representation
206     */
207    @Override
208    public String toString() {
209        final StringBuilder buffer = new StringBuilder();
210        buffer.append(super.toString());
211        buffer.append("(");
212        append(prefixes, buffer);
213        buffer.append(")");
214        return buffer.toString();
215    }
216
217}