    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 *      https://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}