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.math.BigInteger; 022import java.nio.file.FileVisitResult; 023import java.nio.file.Files; 024import java.nio.file.Path; 025import java.nio.file.attribute.BasicFileAttributes; 026import java.util.Objects; 027 028import org.apache.commons.io.file.Counters.PathCounters; 029import org.apache.commons.io.filefilter.IOFileFilter; 030import org.apache.commons.io.filefilter.SymbolicLinkFileFilter; 031import org.apache.commons.io.filefilter.TrueFileFilter; 032import org.apache.commons.io.function.IOBiFunction; 033 034/** 035 * Counts files, directories, and sizes, as a visit proceeds. 036 * 037 * @since 2.7 038 */ 039public class CountingPathVisitor extends SimplePathVisitor { 040 041 static final String[] EMPTY_STRING_ARRAY = {}; 042 043 static IOFileFilter defaultDirFilter() { 044 return TrueFileFilter.INSTANCE; 045 } 046 047 static IOFileFilter defaultFileFilter() { 048 return new SymbolicLinkFileFilter(FileVisitResult.TERMINATE, FileVisitResult.CONTINUE); 049 } 050 051 /** 052 * Constructs a new instance configured with a {@link BigInteger} {@link PathCounters}. 053 * 054 * @return a new instance configured with a {@link BigInteger} {@link PathCounters}. 055 */ 056 public static CountingPathVisitor withBigIntegerCounters() { 057 return new CountingPathVisitor(Counters.bigIntegerPathCounters()); 058 } 059 060 /** 061 * Constructs a new instance configured with a {@code long} {@link PathCounters}. 062 * 063 * @return a new instance configured with a {@code long} {@link PathCounters}. 064 */ 065 public static CountingPathVisitor withLongCounters() { 066 return new CountingPathVisitor(Counters.longPathCounters()); 067 } 068 069 private final PathCounters pathCounters; 070 private final PathFilter fileFilter; 071 private final PathFilter dirFilter; 072 073 /** 074 * Constructs a new instance. 075 * 076 * @param pathCounter How to count path visits. 077 */ 078 public CountingPathVisitor(final PathCounters pathCounter) { 079 this(pathCounter, defaultFileFilter(), defaultDirFilter()); 080 } 081 082 /** 083 * Constructs a new instance. 084 * 085 * @param pathCounter How to count path visits. 086 * @param fileFilter Filters which files to count. 087 * @param dirFilter Filters which directories to count. 088 * @since 2.9.0 089 */ 090 public CountingPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) { 091 this.pathCounters = Objects.requireNonNull(pathCounter, "pathCounter"); 092 this.fileFilter = Objects.requireNonNull(fileFilter, "fileFilter"); 093 this.dirFilter = Objects.requireNonNull(dirFilter, "dirFilter"); 094 } 095 096 /** 097 * Constructs a new instance. 098 * 099 * @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 */ 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 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 */ 130 public PathCounters getPathCounters() { 131 return pathCounters; 132 } 133 134 @Override 135 public int hashCode() { 136 return Objects.hash(pathCounters); 137 } 138 139 @Override 140 public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { 141 updateDirCounter(dir, exc); 142 return FileVisitResult.CONTINUE; 143 } 144 145 @Override 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 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 */ 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 */ 173 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) { 174 pathCounters.getFileCounter().increment(); 175 pathCounters.getByteCounter().add(attributes.size()); 176 } 177 178 @Override 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}