1+ /*******************************************************************************
2+ * Copyright (c) 2000, 2020 IBM Corporation and others.
3+ *
4+ * This program and the accompanying materials
5+ * are made available under the terms of the Eclipse Public License 2.0
6+ * which accompanies this distribution, and is available at
7+ * https://www.eclipse.org/legal/epl-2.0/
8+ *
9+ * SPDX-License-Identifier: EPL-2.0
10+ *
11+ *******************************************************************************/
12+ package org .eclipse .pde .internal .core .util ;
13+
14+ import java .io .IOException ;
15+ import java .nio .file .FileVisitResult ;
16+ import java .nio .file .FileVisitor ;
17+ import java .nio .file .Files ;
18+ import java .nio .file .Path ;
19+ import java .nio .file .attribute .BasicFileAttributes ;
20+ import java .util .Objects ;
21+
22+ import org .eclipse .core .runtime .IProgressMonitor ;
23+ import org .eclipse .core .runtime .NullProgressMonitor ;
24+ import org .eclipse .core .runtime .Platform ;
25+ import org .eclipse .core .runtime .SubMonitor ;
26+
27+ /**
28+ * Deletes content of a directory recursively.
29+ */
30+ public class DeleteContentWalker implements FileVisitor <Path > {
31+
32+ /**
33+ * Deletes the given root directory and its content. If the deletion fails
34+ * or is canceled by the user, the method returns silently.
35+ *
36+ * @param root
37+ * The root directory to delete.
38+ * @param monitor
39+ * The monitor to report progress to. Can be null.
40+ */
41+ public static void deleteDirectory (Path root , IProgressMonitor monitor ) {
42+ // if there is no file, no need to proceed
43+ if (root == null || !Files .exists (root )) {
44+ return ;
45+ }
46+
47+ IProgressMonitor submonitor = createSubMonitor (root , monitor );
48+
49+ try {
50+ Files .walkFileTree (root , new DeleteContentWalker (root , submonitor ));
51+ } catch (IOException e ) {
52+ // noting to do
53+ } finally {
54+ submonitor .done ();
55+ }
56+ }
57+
58+ private static IProgressMonitor createSubMonitor (Path root , IProgressMonitor monitor ) {
59+ IProgressMonitor submonitor ;
60+ if (monitor == null || monitor instanceof NullProgressMonitor ) {
61+ submonitor = SubMonitor .convert (monitor );
62+ } else {
63+ try (var stream = Files .list (root )) {
64+ // only use the root content for progress. anything else would
65+ // be overkill.
66+ long count = stream .count ();
67+ submonitor = SubMonitor .convert (monitor , (int ) count );
68+ } catch (IOException e ) {
69+ // In case of error, just ignore the monitor;
70+ submonitor = SubMonitor .convert (monitor );
71+ }
72+ }
73+ return submonitor ;
74+ }
75+
76+ private final Path root ;
77+ private final IProgressMonitor monitor ;
78+
79+ private DeleteContentWalker (Path root , IProgressMonitor monitor ) {
80+ this .root = root ;
81+ this .monitor = monitor ;
82+ }
83+
84+ @ Override
85+ public FileVisitResult preVisitDirectory (Path path , BasicFileAttributes attrs ) throws IOException {
86+ if (Platform .OS .isWindows ()) {
87+ Files .setAttribute (path , "dos:readonly" , false ); //$NON-NLS-1$
88+ }
89+ return resultIfNotCanceled (FileVisitResult .CONTINUE );
90+ }
91+
92+ @ Override
93+ public FileVisitResult visitFile (Path path , BasicFileAttributes attrs ) throws IOException {
94+ if (Platform .OS .isWindows ()) {
95+ Files .setAttribute (path , "dos:readonly" , false ); //$NON-NLS-1$
96+ }
97+ Files .deleteIfExists (path );
98+
99+ if (Objects .equals (path .getParent (), root )) {
100+ monitor .worked (1 );
101+ }
102+ return resultIfNotCanceled (FileVisitResult .CONTINUE );
103+ }
104+
105+ @ Override
106+ public FileVisitResult visitFileFailed (Path file , IOException exc ) throws IOException {
107+ if (exc != null ) {
108+ throw exc ;
109+ }
110+ return resultIfNotCanceled (FileVisitResult .CONTINUE );
111+ }
112+
113+ @ Override
114+ public FileVisitResult postVisitDirectory (Path path , IOException exc ) throws IOException {
115+ Files .deleteIfExists (path );
116+
117+ if (Objects .equals (path .getParent (), root )) {
118+ monitor .worked (1 );
119+ }
120+ return resultIfNotCanceled (FileVisitResult .CONTINUE );
121+ }
122+
123+ /**
124+ * Returns the given result if not canceled. If canceled
125+ * {@link FileVisitResult#TERMINATE} is returned.
126+ */
127+ private FileVisitResult resultIfNotCanceled (FileVisitResult result ) {
128+ if (monitor .isCanceled ()) {
129+ return FileVisitResult .TERMINATE ;
130+ }
131+ return result ;
132+ }
133+ }
0 commit comments