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 * https://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 @SuppressWarnings("deprecation") 199 @Deprecated 200 public AccumulatorPathVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, 201 final IOBiFunction<Path, IOException, FileVisitResult> visitFileFailed) { 202 super(pathCounter, fileFilter, dirFilter, visitFileFailed); 203 } 204 205 private void add(final List<Path> list, final Path dir) { 206 list.add(dir.normalize()); 207 } 208 209 @Override 210 public boolean equals(final Object obj) { 211 if (this == obj) { 212 return true; 213 } 214 if (!super.equals(obj)) { 215 return false; 216 } 217 if (!(obj instanceof AccumulatorPathVisitor)) { 218 return false; 219 } 220 final AccumulatorPathVisitor other = (AccumulatorPathVisitor) obj; 221 return Objects.equals(dirList, other.dirList) && Objects.equals(fileList, other.fileList); 222 } 223 224 /** 225 * Gets a copy of the list of visited directories. 226 * 227 * @return a copy of the list of visited directories. 228 */ 229 public List<Path> getDirList() { 230 return new ArrayList<>(dirList); 231 } 232 233 /** 234 * Gets a copy of the list of visited files. 235 * 236 * @return a copy of the list of visited files. 237 */ 238 public List<Path> getFileList() { 239 return new ArrayList<>(fileList); 240 } 241 242 @Override 243 public int hashCode() { 244 final int prime = 31; 245 int result = super.hashCode(); 246 result = prime * result + Objects.hash(dirList, fileList); 247 return result; 248 } 249 250 /** 251 * Relativizes each directory path with {@link Path#relativize(Path)} against the given {@code parent}, optionally 252 * sorting the result. 253 * 254 * @param parent A parent path 255 * @param sort Whether to sort 256 * @param comparator How to sort, null uses default sorting. 257 * @return A new list 258 */ 259 public List<Path> relativizeDirectories(final Path parent, final boolean sort, 260 final Comparator<? super Path> comparator) { 261 return PathUtils.relativize(getDirList(), parent, sort, comparator); 262 } 263 264 /** 265 * Relativizes each file path with {@link Path#relativize(Path)} against the given {@code parent}, optionally 266 * sorting the result. 267 * 268 * @param parent A parent path 269 * @param sort Whether to sort 270 * @param comparator How to sort, null uses default sorting. 271 * @return A new list 272 */ 273 public List<Path> relativizeFiles(final Path parent, final boolean sort, 274 final Comparator<? super Path> comparator) { 275 return PathUtils.relativize(getFileList(), parent, sort, comparator); 276 } 277 278 @Override 279 protected void updateDirCounter(final Path dir, final IOException exc) { 280 super.updateDirCounter(dir, exc); 281 add(dirList, dir); 282 } 283 284 @Override 285 protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) { 286 super.updateFileCounters(file, attributes); 287 add(fileList, file); 288 } 289 290}