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 * https://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.nio.file.FileVisitResult;
22 import java.nio.file.Path;
23 import java.nio.file.attribute.BasicFileAttributes;
24 import java.util.ArrayList;
25 import java.util.Comparator;
26 import java.util.List;
27 import java.util.Objects;
28
29 import org.apache.commons.io.file.Counters.PathCounters;
30 import org.apache.commons.io.function.IOBiFunction;
31
32 /**
33 * Accumulates normalized paths during visitation.
34 * <p>
35 * Use with care on large file trees as each visited Path element is remembered.
36 * </p>
37 * <h2>Example</h2>
38 *
39 * <pre>
40 * Path dir = PathUtils.current();
41 * // We are interested in files older than one day
42 * Instant cutoff = Instant.now().minus(Duration.ofDays(1));
43 * AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new AgeFileFilter(cutoff));
44 * //
45 * // Walk one directory
46 * Files.walkFileTree(dir, Collections.emptySet(), 1, visitor);
47 * System.out.println(visitor.getPathCounters());
48 * System.out.println(visitor.getFileList());
49 * //
50 * visitor.getPathCounters().reset();
51 * //
52 * // Walk directory tree
53 * Files.walkFileTree(dir, visitor);
54 * System.out.println(visitor.getPathCounters());
55 * System.out.println(visitor.getDirList());
56 * System.out.println(visitor.getFileList());
57 * </pre>
58 *
59 * @since 2.7
60 */
61 public class AccumulatorPathVisitor extends CountingPathVisitor {
62
63 /**
64 * Builds instances of {@link AccumulatorPathVisitor}.
65 *
66 * @since 2.19.0
67 */
68 public static class Builder extends AbstractBuilder<AccumulatorPathVisitor, Builder> {
69
70 /**
71 * Constructs a new builder.
72 */
73 public Builder() {
74 // empty.
75 }
76
77 @Override
78 public AccumulatorPathVisitor get() {
79 return new AccumulatorPathVisitor(this);
80 }
81
82 }
83
84 /**
85 * Builds instances of {@link AccumulatorPathVisitor}.
86 *
87 * @return a new builder.
88 * @since 2.19.0
89 */
90 public static Builder builder() {
91 return new Builder();
92 }
93
94 /**
95 * Constructs a new instance configured with a BigInteger {@link PathCounters}.
96 *
97 * @return a new instance configured with a BigInteger {@link PathCounters}.
98 * @see #builder()
99 * @see Builder
100 */
101 public static AccumulatorPathVisitor withBigIntegerCounters() {
102 return builder().setPathCounters(Counters.bigIntegerPathCounters()).get();
103 }
104
105 /**
106 * Constructs a new instance configured with a BigInteger {@link PathCounters}.
107 *
108 * @param fileFilter Filters files to accumulate and count.
109 * @param dirFilter Filters directories to accumulate and count.
110 * @return a new instance configured with a long {@link PathCounters}.
111 * @see #builder()
112 * @see Builder
113 * @since 2.9.0
114 */
115 public static AccumulatorPathVisitor withBigIntegerCounters(final PathFilter fileFilter, final PathFilter dirFilter) {
116 return builder().setPathCounters(Counters.bigIntegerPathCounters()).setFileFilter(fileFilter).setDirectoryFilter(dirFilter).get();
117 }
118
119 /**
120 * Constructs a new instance configured with a long {@link PathCounters}.
121 *
122 * @return a new instance configured with a long {@link PathCounters}.
123 * @see #builder()
124 * @see Builder
125 */
126 public static AccumulatorPathVisitor withLongCounters() {
127 return builder().setPathCounters(Counters.longPathCounters()).get();
128 }
129
130 /**
131 * Constructs a new instance configured with a long {@link PathCounters}.
132 *
133 * @param fileFilter Filters files to accumulate and count.
134 * @param dirFilter Filters directories to accumulate and count.
135 * @return a new instance configured with a long {@link PathCounters}.
136 * @see #builder()
137 * @see Builder
138 * @since 2.9.0
139 */
140 public static AccumulatorPathVisitor withLongCounters(final PathFilter fileFilter, final PathFilter dirFilter) {
141 return builder().setPathCounters(Counters.longPathCounters()).setFileFilter(fileFilter).setDirectoryFilter(dirFilter).get();
142 }
143
144 private final List<Path> dirList = new ArrayList<>();
145
146 private final List<Path> fileList = new ArrayList<>();
147
148 /**
149 * Constructs a new instance with a noop path counter.
150 *
151 * @since 2.9.0
152 * @deprecated Use {@link #builder()}.
153 */
154 @Deprecated
155 public AccumulatorPathVisitor() {
156 super(Counters.noopPathCounters());
157 }
158
159 private AccumulatorPathVisitor(final Builder builder) {
160 super(builder);
161 }
162
163 /**
164 * Constructs a new instance that counts file system elements.
165 *
166 * @param pathCounter How to count path visits.
167 * @deprecated Use {@link #builder()}.
168 */
169 @Deprecated
170 public AccumulatorPathVisitor(final PathCounters pathCounter) {
171 super(pathCounter);
172 }
173
174 /**
175 * Constructs a new instance.
176 *
177 * @param pathCounter How to count path visits.
178 * @param fileFilter Filters which files to count.
179 * @param dirFilter Filters which directories to count.
180 * @since 2.9.0
181 * @deprecated Use {@link #builder()}.
182 */
183 @Deprecated
184 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) {
185 super(pathCounter, fileFilter, dirFilter);
186 }
187
188 /**
189 * Constructs a new instance.
190 *
191 * @param pathCounter How to count path visits.
192 * @param fileFilter Filters which files to count.
193 * @param dirFilter Filters which directories to count.
194 * @param visitFileFailed Called on {@link #visitFileFailed(Path, IOException)}.
195 * @since 2.12.0
196 * @deprecated Use {@link #builder()}.
197 */
198 @SuppressWarnings("deprecation")
199 @Deprecated
200 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter,
201 final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) {
202 super(pathCounter, fileFilter, dirFilter, visitFileFailed);
203 }
204
205 private void add(final List<Path> list, final Path dir) {
206 list.add(dir.normalize());
207 }
208
209 @Override
210 public boolean equals(final Object obj) {
211 if (this == obj) {
212 return true;
213 }
214 if (!super.equals(obj)) {
215 return false;
216 }
217 if (!(obj instanceof AccumulatorPathVisitor)) {
218 return false;
219 }
220 final AccumulatorPathVisitor other = (AccumulatorPathVisitor) obj;
221 return Objects.equals(dirList, other.dirList) && Objects.equals(fileList, other.fileList);
222 }
223
224 /**
225 * Gets a copy of the list of visited directories.
226 *
227 * @return a copy of the list of visited directories.
228 */
229 public List<Path> getDirList() {
230 return new ArrayList<>(dirList);
231 }
232
233 /**
234 * Gets a copy of the list of visited files.
235 *
236 * @return a copy of the list of visited files.
237 */
238 public List<Path> getFileList() {
239 return new ArrayList<>(fileList);
240 }
241
242 @Override
243 public int hashCode() {
244 final int prime = 31;
245 int result = super.hashCode();
246 result = prime * result + Objects.hash(dirList, fileList);
247 return result;
248 }
249
250 /**
251 * Relativizes each directory path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
252 * sorting the result.
253 *
254 * @param parent A parent path
255 * @param sort Whether to sort
256 * @param comparator How to sort, null uses default sorting.
257 * @return A new list
258 */
259 public List<Path> relativizeDirectories(final Path parent, final boolean sort,
260 final Comparator<? super Path> comparator) {
261 return PathUtils.relativize(getDirList(), parent, sort, comparator);
262 }
263
264 /**
265 * Relativizes each file path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
266 * sorting the result.
267 *
268 * @param parent A parent path
269 * @param sort Whether to sort
270 * @param comparator How to sort, null uses default sorting.
271 * @return A new list
272 */
273 public List<Path> relativizeFiles(final Path parent, final boolean sort,
274 final Comparator<? super Path> comparator) {
275 return PathUtils.relativize(getFileList(), parent, sort, comparator);
276 }
277
278 @Override
279 protected void updateDirCounter(final Path dir, final IOException exc) {
280 super.updateDirCounter(dir, exc);
281 add(dirList, dir);
282 }
283
284 @Override
285 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
286 super.updateFileCounters(file, attributes);
287 add(fileList, file);
288 }
289
290 }