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 directory 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 directory 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 * Builds instances of {@link AccumulatorPathVisitor}. 065 * 066 * @since 2.19.0 067 */ 068 public static class Builder extends AbstractBuilder<AccumulatorPathVisitor, Builder> { 069 070 /** 071 * Constructs a new builder. 072 */ 073 public Builder() { 074 // empty. 075 } 076 077 @Override 078 public AccumulatorPathVisitor get() { 079 return new AccumulatorPathVisitor(this); 080 } 081 082 } 083 084 /** 085 * Builds instances of {@link AccumulatorPathVisitor}. 086 * 087 * @return a new builder. 088 * @since 2.19.0 089 */ 090 public static Builder builder() { 091 return new Builder(); 092 } 093 094 /** 095 * Constructs a new instance configured with a BigInteger {@link PathCounters}. 096 * 097 * @return a new instance configured with a BigInteger {@link PathCounters}. 098 * @see #builder() 099 * @see Builder 100 */ 101 public static AccumulatorPathVisitor withBigIntegerCounters() { 102 return builder().setPathCounters(Counters.bigIntegerPathCounters()).get(); 103 } 104 105 /** 106 * Constructs a new instance configured with a BigInteger {@link PathCounters}. 107 * 108 * @param fileFilter Filters files to accumulate and count. 109 * @param dirFilter Filters directories to accumulate and count. 110 * @return a new instance configured with a long {@link PathCounters}. 111 * @see #builder() 112 * @see Builder 113 * @since 2.9.0 114 */ 115 public static AccumulatorPathVisitor withBigIntegerCounters(final PathFilter fileFilter, final PathFilter dirFilter) { 116 return builder().setPathCounters(Counters.bigIntegerPathCounters()).setFileFilter(fileFilter).setDirectoryFilter(dirFilter).get(); 117 } 118 119 /** 120 * Constructs a new instance configured with a long {@link PathCounters}. 121 * 122 * @return a new instance configured with a long {@link PathCounters}. 123 * @see #builder() 124 * @see Builder 125 */ 126 public static AccumulatorPathVisitor withLongCounters() { 127 return builder().setPathCounters(Counters.longPathCounters()).get(); 128 } 129 130 /** 131 * Constructs a new instance configured with a long {@link PathCounters}. 132 * 133 * @param fileFilter Filters files to accumulate and count. 134 * @param dirFilter Filters directories to accumulate and count. 135 * @return a new instance configured with a long {@link PathCounters}. 136 * @see #builder() 137 * @see Builder 138 * @since 2.9.0 139 */ 140 public static AccumulatorPathVisitor withLongCounters(final PathFilter fileFilter, final PathFilter dirFilter) { 141 return builder().setPathCounters(Counters.longPathCounters()).setFileFilter(fileFilter).setDirectoryFilter(dirFilter).get(); 142 } 143 144 private final List<Path> dirList = new ArrayList<>(); 145 146 private final List<Path> fileList = new ArrayList<>(); 147 148 /** 149 * Constructs a new instance with a noop path counter. 150 * 151 * @since 2.9.0 152 * @deprecated Use {@link #builder()}. 153 */ 154 @Deprecated 155 public AccumulatorPathVisitor() { 156 super(Counters.noopPathCounters()); 157 } 158 159 private AccumulatorPathVisitor(final Builder builder) { 160 super(builder); 161 } 162 163 /** 164 * Constructs a new instance that counts file system elements. 165 * 166 * @param pathCounter How to count path visits. 167 * @deprecated Use {@link #builder()}. 168 */ 169 @Deprecated 170 public AccumulatorPathVisitor(final PathCounters pathCounter) { 171 super(pathCounter); 172 } 173 174 /** 175 * Constructs a new instance. 176 * 177 * @param pathCounter How to count path visits. 178 * @param fileFilter Filters which files to count. 179 * @param dirFilter Filters which directories to count. 180 * @since 2.9.0 181 * @deprecated Use {@link #builder()}. 182 */ 183 @Deprecated 184 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter) { 185 super(pathCounter, fileFilter, dirFilter); 186 } 187 188 /** 189 * Constructs a new instance. 190 * 191 * @param pathCounter How to count path visits. 192 * @param fileFilter Filters which files to count. 193 * @param dirFilter Filters which directories to count. 194 * @param visitFileFailed Called on {@link #visitFileFailed(Path, IOException)}. 195 * @since 2.12.0 196 * @deprecated Use {@link #builder()}. 197 */ 198 @Deprecated 199 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, 200 final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) { 201 super(pathCounter, fileFilter, dirFilter, visitFileFailed); 202 } 203 204 private void add(final List<Path> list, final Path dir) { 205 list.add(dir.normalize()); 206 } 207 208 @Override 209 public boolean equals(final Object obj) { 210 if (this == obj) { 211 return true; 212 } 213 if (!super.equals(obj)) { 214 return false; 215 } 216 if (!(obj instanceof AccumulatorPathVisitor)) { 217 return false; 218 } 219 final AccumulatorPathVisitor other = (AccumulatorPathVisitor) obj; 220 return Objects.equals(dirList, other.dirList) && Objects.equals(fileList, other.fileList); 221 } 222 223 /** 224 * Gets a copy of the list of visited directories. 225 * 226 * @return a copy of the list of visited directories. 227 */ 228 public List<Path> getDirList() { 229 return new ArrayList<>(dirList); 230 } 231 232 /** 233 * Gets a copy of the list of visited files. 234 * 235 * @return a copy of the list of visited files. 236 */ 237 public List<Path> getFileList() { 238 return new ArrayList<>(fileList); 239 } 240 241 @Override 242 public int hashCode() { 243 final int prime = 31; 244 int result = super.hashCode(); 245 result = prime * result + Objects.hash(dirList, fileList); 246 return result; 247 } 248 249 /** 250 * Relativizes each directory path with {@link Path#relativize(Path)} against the given {@code parent}, optionally 251 * sorting the result. 252 * 253 * @param parent A parent path 254 * @param sort Whether to sort 255 * @param comparator How to sort, null uses default sorting. 256 * @return A new list 257 */ 258 public List<Path> relativizeDirectories(final Path parent, final boolean sort, 259 final Comparator<? super Path> comparator) { 260 return PathUtils.relativize(getDirList(), parent, sort, comparator); 261 } 262 263 /** 264 * Relativizes each file path with {@link Path#relativize(Path)} against the given {@code parent}, optionally 265 * sorting the result. 266 * 267 * @param parent A parent path 268 * @param sort Whether to sort 269 * @param comparator How to sort, null uses default sorting. 270 * @return A new list 271 */ 272 public List<Path> relativizeFiles(final Path parent, final boolean sort, 273 final Comparator<? super Path> comparator) { 274 return PathUtils.relativize(getFileList(), parent, sort, comparator); 275 } 276 277 @Override 278 protected void updateDirCounter(final Path dir, final IOException exc) { 279 super.updateDirCounter(dir, exc); 280 add(dirList, dir); 281 } 282 283 @Override 284 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) { 285 super.updateFileCounters(file, attributes); 286 add(fileList, file); 287 } 288 289}