• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.nio.file.FileVisitResult;
22 import java.nio.file.Files;
23 import java.nio.file.LinkOption;
24 import java.nio.file.Path;
25 import java.nio.file.attribute.BasicFileAttributes;
26 import java.util.Arrays;
27 import java.util.Objects;
28 
29 import org.apache.commons.io.file.Counters.PathCounters;
30 
31 /**
32  * Deletes files but not directories as a visit proceeds.
33  *
34  * @since 2.7
35  */
36 public class CleaningPathVisitor extends CountingPathVisitor {
37 
38     /**
39      * Constructs a new instance configured with a BigInteger {@link PathCounters}.
40      *
41      * @return a new instance configured with a BigInteger {@link PathCounters}.
42      */
withBigIntegerCounters()43     public static CountingPathVisitor withBigIntegerCounters() {
44         return new CleaningPathVisitor(Counters.bigIntegerPathCounters());
45     }
46 
47     /**
48      * Constructs a new instance configured with a long {@link PathCounters}.
49      *
50      * @return a new instance configured with a long {@link PathCounters}.
51      */
withLongCounters()52     public static CountingPathVisitor withLongCounters() {
53         return new CleaningPathVisitor(Counters.longPathCounters());
54     }
55 
56     private final String[] skip;
57     private final boolean overrideReadOnly;
58 
59     /**
60      * Constructs a new visitor that deletes files except for the files and directories explicitly given.
61      *
62      * @param pathCounter How to count visits.
63      * @param deleteOption How deletion is handled.
64      * @param skip The files to skip deleting.
65      * @since 2.8.0
66      */
CleaningPathVisitor(final PathCounters pathCounter, final DeleteOption[] deleteOption, final String... skip)67     public CleaningPathVisitor(final PathCounters pathCounter, final DeleteOption[] deleteOption, final String... skip) {
68         super(pathCounter);
69         final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY;
70         Arrays.sort(temp);
71         this.skip = temp;
72         this.overrideReadOnly = StandardDeleteOption.overrideReadOnly(deleteOption);
73     }
74 
75     /**
76      * Constructs a new visitor that deletes files except for the files and directories explicitly given.
77      *
78      * @param pathCounter How to count visits.
79      * @param skip The files to skip deleting.
80      */
CleaningPathVisitor(final PathCounters pathCounter, final String... skip)81     public CleaningPathVisitor(final PathCounters pathCounter, final String... skip) {
82         this(pathCounter, PathUtils.EMPTY_DELETE_OPTION_ARRAY, skip);
83     }
84 
85     /**
86      * Returns true to process the given path, false if not.
87      *
88      * @param path the path to test.
89      * @return true to process the given path, false if not.
90      */
accept(final Path path)91     private boolean accept(final Path path) {
92         return Arrays.binarySearch(skip, Objects.toString(path.getFileName(), null)) < 0;
93     }
94 
95     @Override
equals(final Object obj)96     public boolean equals(final Object obj) {
97         if (this == obj) {
98             return true;
99         }
100         if (!super.equals(obj)) {
101             return false;
102         }
103         if (getClass() != obj.getClass()) {
104             return false;
105         }
106         final CleaningPathVisitor other = (CleaningPathVisitor) obj;
107         return overrideReadOnly == other.overrideReadOnly && Arrays.equals(skip, other.skip);
108     }
109 
110     @Override
hashCode()111     public int hashCode() {
112         final int prime = 31;
113         int result = super.hashCode();
114         result = prime * result + Arrays.hashCode(skip);
115         result = prime * result + Objects.hash(overrideReadOnly);
116         return result;
117     }
118 
119     @Override
preVisitDirectory(final Path dir, final BasicFileAttributes attributes)120     public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
121         super.preVisitDirectory(dir, attributes);
122         return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
123     }
124 
125     @Override
visitFile(final Path file, final BasicFileAttributes attributes)126     public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
127         // Files.deleteIfExists() never follows links, so use LinkOption.NOFOLLOW_LINKS in other calls to Files.
128         if (accept(file) && Files.exists(file, LinkOption.NOFOLLOW_LINKS)) {
129             if (overrideReadOnly) {
130                 PathUtils.setReadOnly(file, false, LinkOption.NOFOLLOW_LINKS);
131             }
132             Files.deleteIfExists(file);
133         }
134         updateFileCounters(file, attributes);
135         return FileVisitResult.CONTINUE;
136     }
137 }