WildcardFileFilter.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.io.filefilter;

  18. import java.io.File;
  19. import java.io.Serializable;
  20. import java.nio.file.FileVisitResult;
  21. import java.nio.file.Path;
  22. import java.nio.file.attribute.BasicFileAttributes;
  23. import java.util.List;
  24. import java.util.Objects;
  25. import java.util.stream.Stream;

  26. import org.apache.commons.io.FilenameUtils;
  27. import org.apache.commons.io.IOCase;
  28. import org.apache.commons.io.build.AbstractSupplier;
  29. import org.apache.commons.io.file.PathUtils;

  30. /**
  31.  * Filters files using the supplied wildcards.
  32.  * <p>
  33.  * This filter selects files and directories based on one or more wildcards. Testing is case-sensitive by default, but this can be configured.
  34.  * </p>
  35.  * <p>
  36.  * 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
  37.  * command lines. The check is case-sensitive by default. See {@link FilenameUtils#wildcardMatchOnSystem(String,String)} for more information.
  38.  * </p>
  39.  * <p>
  40.  * To build an instance, use {@link Builder}.
  41.  * </p>
  42.  * <p>
  43.  * For example:
  44.  * </p>
  45.  * <h2>Using Classic IO</h2>
  46.  *
  47.  * <pre>
  48.  * File dir = FileUtils.current();
  49.  * FileFilter fileFilter = WildcardFileFilter.builder().setWildcards("*test*.java~*~").get();
  50.  * File[] files = dir.listFiles(fileFilter);
  51.  * for (String file : files) {
  52.  *     System.out.println(file);
  53.  * }
  54.  * </pre>
  55.  *
  56.  * <h2>Using NIO</h2>
  57.  *
  58.  * <pre>
  59.  * final Path dir = PathUtils.current();
  60.  * final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(
  61.  *     WildcardFileFilter.builder().setWildcards("*test*.java~*~").get());
  62.  * //
  63.  * // Walk one directory
  64.  * Files.<strong>walkFileTree</strong>(dir, Collections.emptySet(), 1, visitor);
  65.  * System.out.println(visitor.getPathCounters());
  66.  * System.out.println(visitor.getFileList());
  67.  * //
  68.  * visitor.getPathCounters().reset();
  69.  * //
  70.  * // Walk directory tree
  71.  * Files.<strong>walkFileTree</strong>(dir, visitor);
  72.  * System.out.println(visitor.getPathCounters());
  73.  * System.out.println(visitor.getDirList());
  74.  * System.out.println(visitor.getFileList());
  75.  * </pre>
  76.  * <h2>Deprecating Serialization</h2>
  77.  * <p>
  78.  * <em>Serialization is deprecated and will be removed in 3.0.</em>
  79.  * </p>
  80.  *
  81.  * @since 1.3
  82.  */
  83. public class WildcardFileFilter extends AbstractFileFilter implements Serializable {

  84.     /**
  85.      * Builds a new {@link WildcardFileFilter} instance.
  86.      *
  87.      * @since 2.12.0
  88.      */
  89.     public static class Builder extends AbstractSupplier<WildcardFileFilter, Builder> {

  90.         /** The wildcards that will be used to match file names. */
  91.         private String[] wildcards;

  92.         /** Whether the comparison is case-sensitive. */
  93.         private IOCase ioCase = IOCase.SENSITIVE;

  94.         /**
  95.          * Constructs a new builder of {@link WildcardFileFilter}.
  96.          */
  97.         public Builder() {
  98.             // empty
  99.         }

  100.         @Override
  101.         public WildcardFileFilter get() {
  102.             return new WildcardFileFilter(ioCase, wildcards);
  103.         }

  104.         /**
  105.          * Sets how to handle case sensitivity, null means case-sensitive.
  106.          *
  107.          * @param ioCase how to handle case sensitivity, null means case-sensitive.
  108.          * @return {@code this} instance.
  109.          */
  110.         public Builder setIoCase(final IOCase ioCase) {
  111.             this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
  112.             return this;
  113.         }

  114.         /**
  115.          * Sets the list of wildcards to match, not null.
  116.          *
  117.          * @param wildcards the list of wildcards to match, not null.
  118.          * @return {@code this} instance.
  119.          */
  120.         public Builder setWildcards(final List<String> wildcards) {
  121.             setWildcards(requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
  122.             return this;
  123.         }

  124.         /**
  125.          * Sets the wildcards to match, not null.
  126.          *
  127.          * @param wildcards the wildcards to match, not null.
  128.          * @return {@code this} instance.
  129.          */
  130.         public Builder setWildcards(final String... wildcards) {
  131.             this.wildcards = requireWildcards(wildcards);
  132.             return this;
  133.         }

  134.     }

  135.     private static final long serialVersionUID = -7426486598995782105L;

  136.     /**
  137.      * Constructs a new {@link Builder}.
  138.      *
  139.      * @return a new {@link Builder}.
  140.      * @since 2.12.0
  141.      */
  142.     public static Builder builder() {
  143.         return new Builder();
  144.     }

  145.     private static <T> T requireWildcards(final T wildcards) {
  146.         return Objects.requireNonNull(wildcards, "wildcards");
  147.     }

  148.     /** The wildcards that will be used to match file names. */
  149.     private final String[] wildcards;

  150.     /** Whether the comparison is case-sensitive. */
  151.     private final IOCase ioCase;

  152.     /**
  153.      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
  154.      *
  155.      * @param wildcards the array of wildcards to match, not null
  156.      * @param ioCase    how to handle case sensitivity, null means case-sensitive
  157.      * @throws NullPointerException if the pattern array is null
  158.      */
  159.     private WildcardFileFilter(final IOCase ioCase, final String... wildcards) {
  160.         this.wildcards = requireWildcards(wildcards).clone();
  161.         this.ioCase = IOCase.value(ioCase, IOCase.SENSITIVE);
  162.     }

  163.     /**
  164.      * Constructs a new case-sensitive wildcard filter for a list of wildcards.
  165.      *
  166.      * @param wildcards the list of wildcards to match, not null
  167.      * @throws IllegalArgumentException if the pattern list is null
  168.      * @throws ClassCastException       if the list does not contain Strings
  169.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  170.      */
  171.     @Deprecated
  172.     public WildcardFileFilter(final List<String> wildcards) {
  173.         this(wildcards, IOCase.SENSITIVE);
  174.     }

  175.     /**
  176.      * Constructs a new wildcard filter for a list of wildcards specifying case-sensitivity.
  177.      *
  178.      * @param wildcards the list of wildcards to match, not null
  179.      * @param ioCase    how to handle case sensitivity, null means case-sensitive
  180.      * @throws IllegalArgumentException if the pattern list is null
  181.      * @throws ClassCastException       if the list does not contain Strings
  182.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  183.      */
  184.     @Deprecated
  185.     public WildcardFileFilter(final List<String> wildcards, final IOCase ioCase) {
  186.         this(ioCase, requireWildcards(wildcards).toArray(EMPTY_STRING_ARRAY));
  187.     }

  188.     /**
  189.      * Constructs a new case-sensitive wildcard filter for a single wildcard.
  190.      *
  191.      * @param wildcard the wildcard to match
  192.      * @throws IllegalArgumentException if the pattern is null
  193.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  194.      */
  195.     @Deprecated
  196.     public WildcardFileFilter(final String wildcard) {
  197.         this(IOCase.SENSITIVE, requireWildcards(wildcard));
  198.     }

  199.     /**
  200.      * Constructs a new case-sensitive wildcard filter for an array of wildcards.
  201.      *
  202.      * @param wildcards the array of wildcards to match
  203.      * @throws NullPointerException if the pattern array is null
  204.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  205.      */
  206.     @Deprecated
  207.     public WildcardFileFilter(final String... wildcards) {
  208.         this(IOCase.SENSITIVE, wildcards);
  209.     }

  210.     /**
  211.      * Constructs a new wildcard filter for a single wildcard specifying case-sensitivity.
  212.      *
  213.      * @param wildcard the wildcard to match, not null
  214.      * @param ioCase   how to handle case sensitivity, null means case-sensitive
  215.      * @throws NullPointerException if the pattern is null
  216.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  217.      */
  218.     @Deprecated
  219.     public WildcardFileFilter(final String wildcard, final IOCase ioCase) {
  220.         this(ioCase, wildcard);
  221.     }

  222.     /**
  223.      * Constructs a new wildcard filter for an array of wildcards specifying case-sensitivity.
  224.      *
  225.      * @param wildcards the array of wildcards to match, not null
  226.      * @param ioCase    how to handle case sensitivity, null means case-sensitive
  227.      * @throws NullPointerException if the pattern array is null
  228.      * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
  229.      */
  230.     @Deprecated
  231.     public WildcardFileFilter(final String[] wildcards, final IOCase ioCase) {
  232.         this(ioCase, wildcards);
  233.     }

  234.     /**
  235.      * Checks to see if the file name matches one of the wildcards.
  236.      *
  237.      * @param file the file to check
  238.      * @return true if the file name matches one of the wildcards
  239.      */
  240.     @Override
  241.     public boolean accept(final File file) {
  242.         return accept(file.getName());
  243.     }

  244.     /**
  245.      * Checks to see if the file name matches one of the wildcards.
  246.      *
  247.      * @param dir  the file directory (ignored)
  248.      * @param name the file name
  249.      * @return true if the file name matches one of the wildcards
  250.      */
  251.     @Override
  252.     public boolean accept(final File dir, final String name) {
  253.         return accept(name);
  254.     }

  255.     /**
  256.      * Checks to see if the file name matches one of the wildcards.
  257.      *
  258.      * @param path the file to check
  259.      * @param attributes the path's basic attributes (may be null).
  260.      * @return true if the file name matches one of the wildcards.
  261.      * @since 2.9.0
  262.      */
  263.     @Override
  264.     public FileVisitResult accept(final Path path, final BasicFileAttributes attributes) {
  265.         return toFileVisitResult(accept(PathUtils.getFileNameString(path)));
  266.     }

  267.     private boolean accept(final String name) {
  268.         return Stream.of(wildcards).anyMatch(wildcard -> FilenameUtils.wildcardMatch(name, wildcard, ioCase));
  269.     }

  270.     /**
  271.      * Provide a String representation of this file filter.
  272.      *
  273.      * @return a String representation
  274.      */
  275.     @Override
  276.     public String toString() {
  277.         final StringBuilder buffer = new StringBuilder();
  278.         buffer.append(super.toString());
  279.         buffer.append("(");
  280.         append(wildcards, buffer);
  281.         buffer.append(")");
  282.         return buffer.toString();
  283.     }
  284. }