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 @Deprecated
199 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter,
200 final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) {
201 super(pathCounter, fileFilter, dirFilter, visitFileFailed);
202 }
203
204 private void add(final List<Path> list, final Path dir) {
205 list.add(dir.normalize());
206 }
207
208 @Override
209 public boolean equals(final Object obj) {
210 if (this == obj) {
211 return true;
212 }
213 if (!super.equals(obj)) {
214 return false;
215 }
216 if (!(obj instanceof AccumulatorPathVisitor)) {
217 return false;
218 }
219 final AccumulatorPathVisitor other = (AccumulatorPathVisitor) obj;
220 return Objects.equals(dirList, other.dirList) && Objects.equals(fileList, other.fileList);
221 }
222
223 /**
224 * Gets a copy of the list of visited directories.
225 *
226 * @return a copy of the list of visited directories.
227 */
228 public List<Path> getDirList() {
229 return new ArrayList<>(dirList);
230 }
231
232 /**
233 * Gets a copy of the list of visited files.
234 *
235 * @return a copy of the list of visited files.
236 */
237 public List<Path> getFileList() {
238 return new ArrayList<>(fileList);
239 }
240
241 @Override
242 public int hashCode() {
243 final int prime = 31;
244 int result = super.hashCode();
245 result = prime * result + Objects.hash(dirList, fileList);
246 return result;
247 }
248
249 /**
250 * Relativizes each directory path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
251 * sorting the result.
252 *
253 * @param parent A parent path.
254 * @param sort Whether to sort.
255 * @param comparator How to sort, null uses default sorting.
256 * @return A new list.
257 */
258 public List<Path> relativizeDirectories(final Path parent, final boolean sort,
259 final Comparator<? super Path> comparator) {
260 return PathUtils.relativize(getDirList(), parent, sort, comparator);
261 }
262
263 /**
264 * Relativizes each file path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
265 * sorting the result.
266 *
267 * @param parent A parent path.
268 * @param sort Whether to sort.
269 * @param comparator How to sort, null uses default sorting.
270 * @return A new list.
271 */
272 public List<Path> relativizeFiles(final Path parent, final boolean sort,
273 final Comparator<? super Path> comparator) {
274 return PathUtils.relativize(getFileList(), parent, sort, comparator);
275 }
276
277 @Override
278 protected void updateDirCounter(final Path dir, final IOException exc) {
279 super.updateDirCounter(dir, exc);
280 add(dirList, dir);
281 }
282
283 @Override
284 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
285 super.updateFileCounters(file, attributes);
286 add(fileList, file);
287 }
288
289 }