View Javadoc
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  
18  package org.apache.commons.io.file;
19  
20  import java.io.IOException;
21  import java.math.BigInteger;
22  import java.nio.file.FileVisitResult;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.nio.file.attribute.BasicFileAttributes;
26  import java.util.Objects;
27  
28  import org.apache.commons.io.file.Counters.PathCounters;
29  import org.apache.commons.io.filefilter.IOFileFilter;
30  import org.apache.commons.io.filefilter.SymbolicLinkFileFilter;
31  import org.apache.commons.io.filefilter.TrueFileFilter;
32  import org.apache.commons.io.function.IOBiFunction;
33  
34  /**
35   * Counts files, directories, and sizes, as a visit proceeds.
36   *
37   * @since 2.7
38   */
39  public class CountingPathVisitor extends SimplePathVisitor {
40  
41      static final String[] EMPTY_STRING_ARRAY = {};
42  
43      static IOFileFilter defaultDirFilter() {
44          return TrueFileFilter.INSTANCE;
45      }
46  
47      static IOFileFilter defaultFileFilter() {
48          return new SymbolicLinkFileFilter(FileVisitResult.TERMINATE, FileVisitResult.CONTINUE);
49      }
50  
51      /**
52       * Constructs a new instance configured with a {@link BigInteger} {@link PathCounters}.
53       *
54       * @return a new instance configured with a {@link BigInteger} {@link PathCounters}.
55       */
56      public static CountingPathVisitor withBigIntegerCounters() {
57          return new CountingPathVisitor(Counters.bigIntegerPathCounters());
58      }
59  
60      /**
61       * Constructs a new instance configured with a {@code long} {@link PathCounters}.
62       *
63       * @return a new instance configured with a {@code long} {@link PathCounters}.
64       */
65      public static CountingPathVisitor withLongCounters() {
66          return new CountingPathVisitor(Counters.longPathCounters());
67      }
68  
69      private final PathCounters pathCounters;
70      private final PathFilter fileFilter;
71      private final PathFilter dirFilter;
72  
73      /**
74       * Constructs a new instance.
75       *
76       * @param pathCounter How to count path visits.
77       */
78      public CountingPathVisitor(final PathCounters pathCounter) {
79          this(pathCounter, defaultFileFilter(), defaultDirFilter());
80      }
81  
82      /**
83       * Constructs a new instance.
84       *
85       * @param pathCounter How to count path visits.
86       * @param fileFilter Filters which files to count.
87       * @param dirFilter Filters which directories to count.
88       * @since 2.9.0
89       */
90      public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) {
91          this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter");
92          this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter");
93          this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter");
94      }
95  
96      /**
97       * Constructs a new instance.
98       *
99       * @param pathCounter How to count path visits.
100      * @param fileFilter Filters which files to count.
101      * @param dirFilter Filters which directories to count.
102      * @param visitFileFailed Called on {@link #visitFileFailed(Path, IOException)}.
103      * @since 2.12.0
104      */
105     public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter,
106         final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) {
107         super(visitFileFailed);
108         this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter");
109         this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter");
110         this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter");
111     }
112 
113     @Override
114     public boolean equals(final Object obj) {
115         if (this == obj) {
116             return true;
117         }
118         if (!(obj instanceof CountingPathVisitor)) {
119             return false;
120         }
121         final CountingPathVisitor other = (CountingPathVisitor) obj;
122         return Objects.equals(pathCounters, other.pathCounters);
123     }
124 
125     /**
126      * Gets the visitation counts.
127      *
128      * @return the visitation counts.
129      */
130     public PathCounters getPathCounters() {
131         return pathCounters;
132     }
133 
134     @Override
135     public int hashCode() {
136         return Objects.hash(pathCounters);
137     }
138 
139     @Override
140     public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
141         updateDirCounter(dir, exc);
142         return FileVisitResult.CONTINUE;
143     }
144 
145     @Override
146     public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
147         final FileVisitResult accept = dirFilter.accept(dir, attributes);
148         return accept != FileVisitResult.CONTINUE ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
149     }
150 
151     @Override
152     public String toString() {
153         return pathCounters.toString();
154     }
155 
156     /**
157      * Updates the counter for visiting the given directory.
158      *
159      * @param dir the visited directory.
160      * @param exc Encountered exception.
161      * @since 2.9.0
162      */
163     protected void updateDirCounter(final Path dir, final IOException exc) {
164         pathCounters.getDirectoryCounter().increment();
165     }
166 
167     /**
168      * Updates the counters for visiting the given file.
169      *
170      * @param file the visited file.
171      * @param attributes the visited file attributes.
172      */
173     protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
174         pathCounters.getFileCounter().increment();
175         pathCounters.getByteCounter().add(attributes.size());
176     }
177 
178     @Override
179     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
180         // Note: A file can be a symbolic link to a directory.
181         if (Files.exists(file) && fileFilter.accept(file, attributes) == FileVisitResult.CONTINUE) {
182             updateFileCounters(file, attributes);
183         }
184         return FileVisitResult.CONTINUE;
185     }
186 
187 }