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 */
017
018package org.apache.commons.io.file;
019
020import java.io.IOException;
021import java.nio.file.FileVisitResult;
022import java.nio.file.Path;
023import java.nio.file.attribute.BasicFileAttributes;
024import java.util.ArrayList;
025import java.util.Comparator;
026import java.util.List;
027import java.util.Objects;
028
029import org.apache.commons.io.file.Counters.PathCounters;
030import org.apache.commons.io.function.IOBiFunction;
031
032/**
033 * Accumulates normalized paths during visitation.
034 * <p>
035 * Use with care on large file trees as each visited Path element is remembered.
036 * </p>
037 * <h2>Example</h2>
038 *
039 * <pre>
040 * Path dir = PathUtils.current();
041 * // We are interested in files older than one day
042 * Instant cutoff = Instant.now().minus(Duration.ofDays(1));
043 * AccumulatorPathVisitor visitor = AccumulatorPathVisitor.withLongCounters(new AgeFileFilter(cutoff));
044 * //
045 * // Walk one directory
046 * Files.walkFileTree(dir, Collections.emptySet(), 1, visitor);
047 * System.out.println(visitor.getPathCounters());
048 * System.out.println(visitor.getFileList());
049 * //
050 * visitor.getPathCounters().reset();
051 * //
052 * // Walk directory tree
053 * Files.walkFileTree(dir, visitor);
054 * System.out.println(visitor.getPathCounters());
055 * System.out.println(visitor.getDirList());
056 * System.out.println(visitor.getFileList());
057 * </pre>
058 *
059 * @since 2.7
060 */
061public class AccumulatorPathVisitor extends CountingPathVisitor {
062
063    /**
064     * Builds instances of {@link AccumulatorPathVisitor}.
065     *
066     * @since 2.19.0
067     */
068    public static class Builder extends AbstractBuilder<AccumulatorPathVisitor, Builder> {
069
070        /**
071         * Constructs a new builder.
072         */
073        public Builder() {
074            // empty.
075        }
076
077        @Override
078        public AccumulatorPathVisitor get() {
079            return new AccumulatorPathVisitor(this);
080        }
081
082    }
083
084    /**
085     * Builds instances of {@link AccumulatorPathVisitor}.
086     *
087     * @return a new builder.
088     * @since 2.19.0
089     */
090    public static Builder builder() {
091        return new Builder();
092    }
093
094    /**
095     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
096     *
097     * @return a new instance configured with a BigInteger {@link PathCounters}.
098     * @see #builder()
099     * @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}