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