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 dir
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 dir 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     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
065     *
066     * @return a new instance configured with a BigInteger {@link PathCounters}.
067     */
068    public static AccumulatorPathVisitor withBigIntegerCounters() {
069        return new AccumulatorPathVisitor(Counters.bigIntegerPathCounters());
070    }
071
072    /**
073     * Constructs a new instance configured with a BigInteger {@link PathCounters}.
074     *
075     * @param fileFilter Filters files to accumulate and count.
076     * @param dirFilter Filters directories to accumulate and count.
077     * @return a new instance configured with a long {@link PathCounters}.
078     * @since 2.9.0
079     */
080    public static AccumulatorPathVisitor withBigIntegerCounters(final PathFilter fileFilter,
081        final PathFilter dirFilter) {
082        return new AccumulatorPathVisitor(Counters.bigIntegerPathCounters(), fileFilter, dirFilter);
083    }
084
085    /**
086     * Constructs a new instance configured with a long {@link PathCounters}.
087     *
088     * @return a new instance configured with a long {@link PathCounters}.
089     */
090    public static AccumulatorPathVisitor withLongCounters() {
091        return new AccumulatorPathVisitor(Counters.longPathCounters());
092    }
093
094    /**
095     * Constructs a new instance configured with a long {@link PathCounters}.
096     *
097     * @param fileFilter Filters files to accumulate and count.
098     * @param dirFilter Filters directories to accumulate and count.
099     * @return a new instance configured with a long {@link PathCounters}.
100     * @since 2.9.0
101     */
102    public static AccumulatorPathVisitor withLongCounters(final PathFilter fileFilter, final PathFilter dirFilter) {
103        return new AccumulatorPathVisitor(Counters.longPathCounters(), fileFilter, dirFilter);
104    }
105
106    private final List<Path> dirList = new ArrayList<>();
107
108    private final List<Path> fileList = new ArrayList<>();
109
110    /**
111     * Constructs a new instance.
112     *
113     * @since 2.9.0
114     */
115    public AccumulatorPathVisitor() {
116        super(Counters.noopPathCounters());
117    }
118
119    /**
120     * Constructs a new instance that counts file system elements.
121     *
122     * @param pathCounter How to count path visits.
123     */
124    public AccumulatorPathVisitor(final PathCounters pathCounter) {
125        super(pathCounter);
126    }
127
128    /**
129     * Constructs a new instance.
130     *
131     * @param pathCounter How to count path visits.
132     * @param fileFilter Filters which files to count.
133     * @param dirFilter Filters which directories to count.
134     * @since 2.9.0
135     */
136    public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) {
137        super(pathCounter, fileFilter, dirFilter);
138    }
139
140    /**
141     * Constructs a new instance.
142     *
143     * @param pathCounter How to count path visits.
144     * @param fileFilter Filters which files to count.
145     * @param dirFilter Filters which directories to count.
146     * @param visitFileFailed Called on {@link #visitFileFailed(Path, IOException)}.
147     * @since 2.12.0
148     */
149    public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter,
150        final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) {
151        super(pathCounter, fileFilter, dirFilter, visitFileFailed);
152    }
153
154    private void add(final List<Path> list, final Path dir) {
155        list.add(dir.normalize());
156    }
157
158    @Override
159    public boolean equals(final Object obj) {
160        if (this == obj) {
161            return true;
162        }
163        if (!super.equals(obj)) {
164            return false;
165        }
166        if (!(obj instanceof AccumulatorPathVisitor)) {
167            return false;
168        }
169        final AccumulatorPathVisitor other = (AccumulatorPathVisitor) obj;
170        return Objects.equals(dirList, other.dirList) && Objects.equals(fileList, other.fileList);
171    }
172
173    /**
174     * Gets a copy of the list of visited directories.
175     *
176     * @return a copy of the list of visited directories.
177     */
178    public List<Path> getDirList() {
179        return new ArrayList<>(dirList);
180    }
181
182    /**
183     * Gets a copy of the list of visited files.
184     *
185     * @return a copy of the list of visited files.
186     */
187    public List<Path> getFileList() {
188        return new ArrayList<>(fileList);
189    }
190
191    @Override
192    public int hashCode() {
193        final int prime = 31;
194        int result = super.hashCode();
195        result = prime * result + Objects.hash(dirList, fileList);
196        return result;
197    }
198
199    /**
200     * Relativizes each directory path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
201     * sorting the result.
202     *
203     * @param parent A parent path
204     * @param sort Whether to sort
205     * @param comparator How to sort, null uses default sorting.
206     * @return A new list
207     */
208    public List<Path> relativizeDirectories(final Path parent, final boolean sort,
209        final Comparator<? super Path> comparator) {
210        return PathUtils.relativize(getDirList(), parent, sort, comparator);
211    }
212
213    /**
214     * Relativizes each file path with {@link Path#relativize(Path)} against the given {@code parent}, optionally
215     * sorting the result.
216     *
217     * @param parent A parent path
218     * @param sort Whether to sort
219     * @param comparator How to sort, null uses default sorting.
220     * @return A new list
221     */
222    public List<Path> relativizeFiles(final Path parent, final boolean sort,
223        final Comparator<? super Path> comparator) {
224        return PathUtils.relativize(getFileList(), parent, sort, comparator);
225    }
226
227    @Override
228    protected void updateDirCounter(final Path dir, final IOException exc) {
229        super.updateDirCounter(dir, exc);
230        add(dirList, dir);
231    }
232
233    @Override
234    protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
235        super.updateFileCounters(file, attributes);
236        add(fileList, file);
237    }
238
239}