1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.io.file; 19 20 import java.io.IOException; 21 import java.math.BigInteger; 22 import java.nio.file.FileVisitResult; 23 import java.nio.file.Files; 24 import java.nio.file.Path; 25 import java.nio.file.attribute.BasicFileAttributes; 26 import java.util.Objects; 27 28 import org.apache.commons.io.file.Counters.PathCounters; 29 import org.apache.commons.io.filefilter.IOFileFilter; 30 import org.apache.commons.io.filefilter.SymbolicLinkFileFilter; 31 import org.apache.commons.io.filefilter.TrueFileFilter; 32 import org.apache.commons.io.function.IOBiFunction; 33 34 /** 35 * Counts files, directories, and sizes, as a visit proceeds. 36 * 37 * @since 2.7 38 */ 39 public class CountingPathVisitor extends SimplePathVisitor { 40 41 static final String[] EMPTY_STRING_ARRAY = {}; 42 defaultDirFilter()43 static IOFileFilter defaultDirFilter() { 44 return TrueFileFilter.INSTANCE; 45 } 46 defaultFileFilter()47 static IOFileFilter defaultFileFilter() { 48 return new SymbolicLinkFileFilter(FileVisitResult.TERMINATE, FileVisitResult.CONTINUE); 49 } 50 51 /** 52 * Creates a new instance configured with a {@link BigInteger} {@link PathCounters}. 53 * 54 * @return a new instance configured with a {@link BigInteger} {@link PathCounters}. 55 */ withBigIntegerCounters()56 public static CountingPathVisitor withBigIntegerCounters() { 57 return new CountingPathVisitor(Counters.bigIntegerPathCounters()); 58 } 59 60 /** 61 * Creates a new instance configured with a {@code long} {@link PathCounters}. 62 * 63 * @return a new instance configured with a {@code long} {@link PathCounters}. 64 */ withLongCounters()65 public static CountingPathVisitor withLongCounters() { 66 return new CountingPathVisitor(Counters.longPathCounters()); 67 } 68 69 private final PathCounters pathCounters; 70 private final PathFilter fileFilter; 71 private final PathFilter dirFilter; 72 73 /** 74 * Constructs a new instance. 75 * 76 * @param pathCounter How to count path visits. 77 */ CountingPathVisitor(final PathCounters pathCounter)78 public CountingPathVisitor(final PathCounters pathCounter) { 79 this(pathCounter, defaultFileFilter(), defaultDirFilter()); 80 } 81 82 /** 83 * Constructs a new instance. 84 * 85 * @param pathCounter How to count path visits. 86 * @param fileFilter Filters which files to count. 87 * @param dirFilter Filters which directories to count. 88 * @since 2.9.0 89 */ CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter)90 public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) { 91 this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter"); 92 this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter"); 93 this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter"); 94 } 95 96 /** 97 * Constructs a new instance. 98 * 99 * @param pathCounter How to count path visits. 100 * @param fileFilter Filters which files to count. 101 * @param dirFilter Filters which directories to count. 102 * @param visitFileFailed Called on {@link #visitFileFailed(Path, IOException)}. 103 * @since 2.12.0 104 */ CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed)105 public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, 106 final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) { 107 super(visitFileFailed); 108 this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter"); 109 this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter"); 110 this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter"); 111 } 112 113 @Override equals(final Object obj)114 public boolean equals(final Object obj) { 115 if (this == obj) { 116 return true; 117 } 118 if (!(obj instanceof CountingPathVisitor)) { 119 return false; 120 } 121 final CountingPathVisitor other = (CountingPathVisitor) obj; 122 return Objects.equals(pathCounters, other.pathCounters); 123 } 124 125 /** 126 * Gets the visitation counts. 127 * 128 * @return the visitation counts. 129 */ getPathCounters()130 public PathCounters getPathCounters() { 131 return pathCounters; 132 } 133 134 @Override hashCode()135 public int hashCode() { 136 return Objects.hash(pathCounters); 137 } 138 139 @Override postVisitDirectory(final Path dir, final IOException exc)140 public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { 141 updateDirCounter(dir, exc); 142 return FileVisitResult.CONTINUE; 143 } 144 145 @Override preVisitDirectory(final Path dir, final BasicFileAttributes attributes)146 public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException { 147 final FileVisitResult accept = dirFilter.accept(dir, attributes); 148 return accept != FileVisitResult.CONTINUE ? FileVisitResult.SKIP_SUBTREE : FileVisitResult.CONTINUE; 149 } 150 151 @Override toString()152 public String toString() { 153 return pathCounters.toString(); 154 } 155 156 /** 157 * Updates the counter for visiting the given directory. 158 * 159 * @param dir the visited directory. 160 * @param exc Encountered exception. 161 * @since 2.9.0 162 */ updateDirCounter(final Path dir, final IOException exc)163 protected void updateDirCounter(final Path dir, final IOException exc) { 164 pathCounters.getDirectoryCounter().increment(); 165 } 166 167 /** 168 * Updates the counters for visiting the given file. 169 * 170 * @param file the visited file. 171 * @param attributes the visited file attributes. 172 */ updateFileCounters(final Path file, final BasicFileAttributes attributes)173 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) { 174 pathCounters.getFileCounter().increment(); 175 pathCounters.getByteCounter().add(attributes.size()); 176 } 177 178 @Override visitFile(final Path file, final BasicFileAttributes attributes)179 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException { 180 // Note: A file can be a symbolic link to a directory. 181 if (Files.exists(file) && fileFilter.accept(file, attributes) == FileVisitResult.CONTINUE) { 182 updateFileCounters(file, attributes); 183 } 184 return FileVisitResult.CONTINUE; 185 } 186 187 } 188