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}