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.FilenameUtils; 029import org.apache.commons.io.IOCase; 030import org.apache.commons.io.build.AbstractSupplier; 031 032/** 033 * Filters files using the supplied wildcards. 034 * <p> 035 * This filter selects files and directories based on one or more wildcards. Testing is case-sensitive by default, but this can be configured. 036 * </p> 037 * <p> 038 * The wildcard matcher uses the characters '?' and '*' to represent a single or multiple wildcard characters. This is the same as often found on DOS/Unix 039 * command lines. The check is case-sensitive by default. See {@link FilenameUtils#wildcardMatchOnSystem(String,String)} for more information. 040 * </p> 041 * <p> 042 * To build an instance, see {@link Builder}. 043 * </p> 044 * <p> 045 * For example: 046 * </p> 047 * <h2>Using Classic IO</h2> 048 * 049 * <pre> 050 * File dir = FileUtils.current(); 051 * FileFilter fileFilter = WildcardFileFilter.builder().setWildcards("*test*.java~*~").get(); 052 * File[] files = dir.listFiles(fileFilter); 053 * for (String file : files) { 054 * System.out.println(file); 055 * } 056 * </pre> 057 * 058 * <h2>Using NIO</h2> 059 * 060 * <pre> 061 * final Path dir = PathUtils.current(); 062 * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters( 063 * WildcardFileFilter.builder().setWildcards("*test*.java~*~").get()); 064 * // 065 * // Walk one dir 066 * Files.<b>walkFileTree</b>(dir, Collections.emptySet(), 1, visitor); 067 * System.out.println(visitor.getPathCounters()); 068 * System.out.println(visitor.getFileList()); 069 * // 070 * visitor.getPathCounters().reset(); 071 * // 072 * // Walk dir tree 073 * Files.<b>walkFileTree</b>(dir, visitor); 074 * System.out.println(visitor.getPathCounters()); 075 * System.out.println(visitor.getDirList()); 076 * System.out.println(visitor.getFileList()); 077 * </pre> 078 * <h2>Deprecating Serialization</h2> 079 * <p> 080 * <em>Serialization is deprecated and will be removed in 3.0.</em> 081 * </p> 082 * 083 * @since 1.3 084 */ 085public class WildcardFileFilter extends AbstractFileFilter implements Serializable { 086 087 /** 088 * Builds a new {@link WildcardFileFilter} instance. 089 * 090 * @since 2.12.0 091 */ 092 public static class Builder extends AbstractSupplier<WildcardFileFilter, Builder> { 093 094 /** The wildcards that will be used to match file names. */ 095 private String[] wildcards; 096 097 /** Whether the comparison is case-sensitive. */ 098 private IOCase ioCase = IOCase.SENSITIVE; 099 100 @Override 101 public WildcardFileFilter get() { 102 return new WildcardFileFilter(ioCase, wildcards); 103 } 104 105 /** 106 * Sets how to handle case sensitivity, null means case-sensitive. 107 * 108 * @param ioCase how to handle case sensitivity, null means case-sensitive. 109 * @return this 110 */ 111 public Builder setIoCase(final IOCase ioCase) { 112 this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE); 113 return this; 114 } 115 116 /** 117 * Sets the list of wildcards to match, not null. 118 * 119 * @param wildcards the list of wildcards to match, not null. 120 * @return this 121 */ 122 public Builder setWildcards(final List<String> wildcards) { 123 setWildcards(requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY)); 124 return this; 125 } 126 127 /** 128 * Sets the wildcards to match, not null. 129 * 130 * @param wildcards the wildcards to match, not null. 131 * @return this 132 */ 133 public Builder setWildcards(final String... wildcards) { 134 this.wildcards = requireWildcards(wildcards); 135 return this; 136 } 137 138 } 139 140 private static final long serialVersionUID = -7426486598995782105L; 141 142 /** 143 * Constructs a new {@link Builder}. 144 * 145 * @return a new {@link Builder}. 146 * @since 2.12.0 147 */ 148 public static Builder builder() { 149 return new Builder(); 150 } 151 152 private static <T> T requireWildcards(final T wildcards) { 153 return Objects.requireNonNull(wildcards, "wildcards"); 154 } 155 156 /** The wildcards that will be used to match file names. */ 157 private final String[] wildcards; 158 159 /** Whether the comparison is case-sensitive. */ 160 private final IOCase ioCase; 161 162 /** 163 * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity. 164 * 165 * @param wildcards the array of wildcards to match, not null 166 * @param ioCase how to handle case sensitivity, null means case-sensitive 167 * @throws NullPointerException if the pattern array is null 168 */ 169 private WildcardFileFilter(final IOCase ioCase, final String... wildcards) { 170 this.wildcards = requireWildcards(wildcards).clone(); 171 this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE); 172 } 173 174 /** 175 * Constructs a new case-sensitive wildcard filter for a list of wildcards. 176 * 177 * @param wildcards the list of wildcards to match, not null 178 * @throws IllegalArgumentException if the pattern list is null 179 * @throws ClassCastException if the list does not contain Strings 180 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 181 */ 182 @Deprecated 183 public WildcardFileFilter(final List<String> wildcards) { 184 this(wildcards, IOCase.SENSITIVE); 185 } 186 187 /** 188 * Constructs a new wildcard filter for a list of wildcards specifying case-sensitivity. 189 * 190 * @param wildcards the list of wildcards to match, not null 191 * @param ioCase how to handle case sensitivity, null means case-sensitive 192 * @throws IllegalArgumentException if the pattern list is null 193 * @throws ClassCastException if the list does not contain Strings 194 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 195 */ 196 @Deprecated 197 public WildcardFileFilter(final List<String> wildcards, final IOCase ioCase) { 198 this(ioCase, requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY)); 199 } 200 201 /** 202 * Constructs a new case-sensitive wildcard filter for a single wildcard. 203 * 204 * @param wildcard the wildcard to match 205 * @throws IllegalArgumentException if the pattern is null 206 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 207 */ 208 @Deprecated 209 public WildcardFileFilter(final String wildcard) { 210 this(IOCase.SENSITIVE, requireWildcards(wildcard)); 211 } 212 213 /** 214 * Constructs a new case-sensitive wildcard filter for an array of wildcards. 215 * 216 * @param wildcards the array of wildcards to match 217 * @throws NullPointerException if the pattern array is null 218 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 219 */ 220 @Deprecated 221 public WildcardFileFilter(final String... wildcards) { 222 this(IOCase.SENSITIVE, wildcards); 223 } 224 225 /** 226 * Constructs a new wildcard filter for a single wildcard specifying case-sensitivity. 227 * 228 * @param wildcard the wildcard to match, not null 229 * @param ioCase how to handle case sensitivity, null means case-sensitive 230 * @throws NullPointerException if the pattern is null 231 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 232 */ 233 @Deprecated 234 public WildcardFileFilter(final String wildcard, final IOCase ioCase) { 235 this(ioCase, wildcard); 236 } 237 238 /** 239 * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity. 240 * 241 * @param wildcards the array of wildcards to match, not null 242 * @param ioCase how to handle case sensitivity, null means case-sensitive 243 * @throws NullPointerException if the pattern array is null 244 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()} 245 */ 246 @Deprecated 247 public WildcardFileFilter(final String[] wildcards, final IOCase ioCase) { 248 this(ioCase, wildcards); 249 } 250 251 /** 252 * Checks to see if the file name matches one of the wildcards. 253 * 254 * @param file the file to check 255 * @return true if the file name matches one of the wildcards 256 */ 257 @Override 258 public boolean accept(final File file) { 259 return accept(file.getName()); 260 } 261 262 /** 263 * Checks to see if the file name matches one of the wildcards. 264 * 265 * @param dir the file directory (ignored) 266 * @param name the file name 267 * @return true if the file name matches one of the wildcards 268 */ 269 @Override 270 public boolean accept(final File dir, final String name) { 271 return accept(name); 272 } 273 274 /** 275 * Checks to see if the file name matches one of the wildcards. 276 * 277 * @param file the file to check 278 * 279 * @return true if the file name matches one of the wildcards. 280 * @since 2.9.0 281 */ 282 @Override 283 public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) { 284 return toFileVisitResult(accept(Objects.toString(file.getFileName(), null))); 285 } 286 287 private boolean accept(final String name) { 288 return Stream.of(wildcards).anyMatch(wildcard -> FilenameUtils.wildcardMatch(name, wildcard, ioCase)); 289 } 290 291 /** 292 * Provide a String representation of this file filter. 293 * 294 * @return a String representation 295 */ 296 @Override 297 public String toString() { 298 final StringBuilder buffer = new StringBuilder(); 299 buffer.append(super.toString()); 300 buffer.append("("); 301 append(wildcards, buffer); 302 buffer.append(")"); 303 return buffer.toString(); 304 } 305}