    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.Files;
    023import java.nio.file.Path;
    024import java.nio.file.attribute.BasicFileAttributes;
    025import java.util.Objects;
    026
    027import org.apache.commons.io.file.Counters.PathCounters;
    028import org.apache.commons.io.filefilter.TrueFileFilter;
    029
    030/**
    031 * Counts files, directories, and sizes, as a visit proceeds.
    032 *
    033 * @since 2.7
    034 */
    035public class CountingPathVisitor extends SimplePathVisitor {
    036
    037    static final String[] EMPTY_STRING_ARRAY = {};
    038
    039    /**
    040     * Creates a new instance configured with a BigInteger {@link PathCounters}.
    041     *
    042     * @return a new instance configured with a BigInteger {@link PathCounters}.
    043     */
    044    public static CountingPathVisitor withBigIntegerCounters() {
    045        return new CountingPathVisitor(Counters.bigIntegerPathCounters());
    046    }
    047
    048    /**
    049     * Creates a new instance configured with a long {@link PathCounters}.
    050     *
    051     * @return a new instance configured with a long {@link PathCounters}.
    052     */
    053    public static CountingPathVisitor withLongCounters() {
    054        return new CountingPathVisitor(Counters.longPathCounters());
    055    }
    056
    057    private final PathCounters pathCounters;
    058    private final PathFilter fileFilter;
    059    private final PathFilter dirFilter;
    060
    061    /**
    062     * Constructs a new instance.
    063     *
    064     * @param pathCounter How to count path visits.
    065     */
    066    public CountingPathVisitor(final PathCounters pathCounter) {
    067        this(pathCounter, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
    068    }
    069
    070    /**
    071     * Constructs a new instance.
    072     *
    073     * @param pathCounter How to count path visits.
    074     * @param fileFilter Filters which files to count.
    075     * @param dirFilter Filters which directories to count.
    076     * @since 2.9.0
    077     */
    078    public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) {
    079        this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter");
    080        this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter");
    081        this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter");
    082    }
    083
    084    @Override
    085    public boolean equals(final Object obj) {
    086        if (this == obj) {
    087            return true;
    088        }
    089        if (!(obj instanceof CountingPathVisitor)) {
    090            return false;
    091        }
    092        final CountingPathVisitor other = (CountingPathVisitor) obj;
    093        return Objects.equals(pathCounters, other.pathCounters);
    094    }
    095
    096    /**
    097     * Gets the visitation counts.
    098     *
    099     * @return the visitation counts.
    100     */
    101    public PathCounters getPathCounters() {
    102        return pathCounters;
    103    }
    104
    105    @Override
    106    public int hashCode() {
    107        return Objects.hash(pathCounters);
    108    }
    109
    110    @Override
    111    public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
    112        updateDirCounter(dir, exc);
    113        return FileVisitResult.CONTINUE;
    114    }
    115
    116    @Override
    117    public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
    118        final FileVisitResult accept = dirFilter.accept(dir, attributes);
    119        return accept != FileVisitResult.CONTINUE ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE;
    120    }
    121
    122    @Override
    123    public String toString() {
    124        return pathCounters.toString();
    125    }
    126
    127    /**
    128     * Updates the counter for visiting the given directory.
    129     *
    130     * @param dir the visited directory.
    131     * @param exc Encountered exception.
    132     * @since 2.9.0
    133     */
    134    protected void updateDirCounter(final Path dir, final IOException exc) {
    135        pathCounters.getDirectoryCounter().increment();
    136    }
    137
    138    /**
    139     * Updates the counters for visiting the given file.
    140     *
    141     * @param file the visited file.
    142     * @param attributes the visited file attributes.
    143     */
    144    protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
    145        pathCounters.getFileCounter().increment();
    146        pathCounters.getByteCounter().add(attributes.size());
    147    }
    148
    149    @Override
    150    public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
    151        if (Files.exists(file) && fileFilter.accept(file, attributes) == FileVisitResult.CONTINUE) {
    152            updateFileCounters(file, attributes);
    153        }
    154        return FileVisitResult.CONTINUE;
    155    }
    156
    157}