    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}