    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    @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}