1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
|
/*
* SonarQube
* Copyright (C) 2009-2025 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.task.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.annotation.Nullable;
import org.sonar.api.utils.ZipUtils;
import org.sonar.core.util.FileUtils;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static java.nio.file.Files.createDirectories;
import static java.util.Objects.requireNonNull;
/**
* This utility class provides Java NIO based replacement for some methods of
* {@link org.apache.commons.io.FileUtils Common IO FileUtils} class.
* Only the methods which name ends with {@code "orThrowIOE"} may raise {@link IOException}.
* Others wrap checked exceptions into {@link IllegalStateException}.
*/
public class Files2 {
public static final Files2 FILES2 = new Files2();
private Files2() {
// use FILES2 singleton
}
/**
* Deletes a directory or a file if it exists, else does nothing. In the case
* of a directly, it is deleted recursively.
*
* @param fileOrDir file or directory to delete
* @throws IOException in case deletion is unsuccessful
*/
public void deleteIfExistsOrThrowIOE(File fileOrDir) throws IOException {
if (!fileOrDir.exists()) {
return;
}
if (fileOrDir.isDirectory()) {
FileUtils.deleteDirectory(fileOrDir);
} else {
Files.delete(fileOrDir.toPath());
}
}
/**
* Like {@link #deleteIfExistsOrThrowIOE(File)} but wraps {@link IOException}
* into {@link IllegalStateException}.
*
* @throws IllegalStateException in case deletion is unsuccessful
*/
public void deleteIfExists(File fileOrDir) {
try {
deleteIfExistsOrThrowIOE(fileOrDir);
} catch (IOException e) {
throw new IllegalStateException("Can not delete " + fileOrDir, e);
}
}
/**
* Deletes a directory or a file if it exists, else does nothing. In the case
* of a directly, it is deleted recursively. Any exception is trapped and
* ignored.
*
* @param fileOrDir file or directory to delete. Can be {@code null}.
*/
public void deleteQuietly(@Nullable File fileOrDir) {
FileUtils.deleteQuietly(fileOrDir);
}
/**
* Moves a file.
*
* <p>
* When the destination file is on another file system, do a "copy and delete".
* </p>
*
* @param from the file to be moved
* @param to the destination file
* @throws NullPointerException if source or destination is {@code null}
* @throws IOException if the destination file exists
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
*/
public void moveFileOrThrowIOE(File from, File to) throws IOException {
org.apache.commons.io.FileUtils.moveFile(from, to);
}
/**
* Like {@link #moveFileOrThrowIOE(File, File)} but wraps {@link IOException}
* into {@link IllegalStateException}.
*
* @param from the file to be moved
* @param to the destination file
* @throws IllegalStateException if the destination file exists
* @throws IllegalStateException if source or destination is invalid
* @throws IllegalStateException if an IO error occurs moving the file
*/
public void moveFile(File from, File to) {
try {
moveFileOrThrowIOE(from, to);
} catch (IOException e) {
throw new IllegalStateException("Can not move file " + from + " to " + to, e);
}
}
/**
* Opens a {@link FileOutputStream} for the specified file, checking and
* creating the parent directory if it does not exist.
* <p>
* The parent directory will be created if it does not exist.
* The file will be created if it does not exist.
*
* @param file the file to open for output, must not be {@code null}
* @param append if {@code true}, then bytes will be added to the
* end of the file rather than overwriting
* @return a new {@link FileOutputStream} for the specified file
* @throws IOException if the specified file is a directory
* @throws IOException if the file can not be written to
* @throws IOException if a parent directory can not be created
*/
public FileOutputStream openOutputStreamOrThrowIOE(File file, boolean append) throws IOException {
if (file.exists()) {
checkOrThrowIOE(!file.isDirectory(), "File %s exists but is a directory", file);
checkOrThrowIOE(file.canWrite(), "File %s can not be written to", file);
} else {
File parent = file.getParentFile();
if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
throw new IOException("Directory " + parent + " could not be created");
}
}
return new FileOutputStream(file, append);
}
/**
* Opens a {@link FileOutputStream} for the specified file, checking and
* creating the parent directory if it does not exist.
* <p>
* The parent directory will be created if it does not exist.
* The file will be created if it does not exist.
*
* @param file the file to open for output, must not be {@code null}
* @param append if {@code true}, then bytes will be added to the
* end of the file rather than overwriting
* @return a new {@link FileOutputStream} for the specified file
* @throws IllegalStateException if the specified file is a directory
* @throws IllegalStateException if the file can not be written to
* @throws IllegalStateException if a parent directory can not be created
*/
public FileOutputStream openOutputStream(File file, boolean append) {
try {
return openOutputStreamOrThrowIOE(file, append);
} catch (IOException e) {
throw new IllegalStateException("Can not open file " + file, e);
}
}
/**
* Opens a {@link FileInputStream} for the specified file, providing better
* error messages than simply calling {@code new FileInputStream(file)}.
*
* @param file the file to open, must not be {@code null}
* @return a new {@link FileInputStream} for the specified file
* @throws IOException if the file does not exist
* @throws IOException if the specified file is a directory
* @throws IOException if the file can not be read
*/
public FileInputStream openInputStreamOrThrowIOE(File file) throws IOException {
checkOrThrowIOE(!file.isDirectory(), "File %s exists but is a directory", file);
checkOrThrowIOE(file.exists(), "File %s does not exist", file);
checkOrThrowIOE(file.canRead(), "File %s can not be read", file);
return new FileInputStream(file);
}
/**
* Opens a {@link FileInputStream} for the specified file, providing better
* error messages than simply calling {@code new FileInputStream(file)}.
*
* @param file the file to open, must not be {@code null}
* @return a new {@link FileInputStream} for the specified file
* @throws IllegalStateException if the file does not exist
* @throws IllegalStateException if the specified file is a directory
* @throws IllegalStateException if the file can not be read
*/
public FileInputStream openInputStream(File file) {
try {
return openInputStreamOrThrowIOE(file);
} catch (IOException e) {
throw new IllegalStateException("Can not open file " + file, e);
}
}
/**
* Unzips a file to the specified directory. The directory is created if it does not exist.
*
* @throws IOException if {@code zipFile} is a directory
* @throws IOException if {@code zipFile} does not exist
* @throws IOException if {@code toDir} can not be created
*/
public void unzipToDirOrThrowIOE(File zipFile, File toDir) throws IOException {
checkOrThrowIOE(!zipFile.isDirectory(), "File %s exists but is a directory", zipFile);
checkOrThrowIOE(zipFile.exists(), "File %s does not exist", zipFile);
ZipUtils.unzip(zipFile, toDir);
}
/**
* Unzips a file to the specified directory. The directory is created if it does not exist.
*
* @throws IllegalStateException if {@code zipFile} is a directory
* @throws IllegalStateException if {@code zipFile} does not exist
* @throws IllegalStateException if {@code toDir} can not be created
*/
public void unzipToDir(File zipFile, File toDir) {
try {
unzipToDirOrThrowIOE(zipFile, toDir);
} catch (IOException e) {
throw new IllegalStateException("Can not unzip file " + zipFile + " to directory " + toDir, e);
}
}
/**
* Zips the directory {@code dir} to the file {@code toFile}. If {@code toFile} is overridden
* if it exists, else it is created.
*
* @throws IllegalStateException if {@code dir} is a not directory
* @throws IllegalStateException if {@code dir} does not exist
* @throws IllegalStateException if {@code toFile} can not be created
*/
public void zipDirOrThrowIOE(File dir, File toFile) throws IOException {
checkOrThrowIOE(dir.exists(), "Directory %s does not exist", dir);
checkOrThrowIOE(dir.isDirectory(), "File %s exists but is not a directory", dir);
ZipUtils.zipDir(dir, toFile);
}
/**
* Zips the directory {@code dir} to the file {@code toFile}. If {@code toFile} is overridden
* if it exists, else it is created.
*
* @throws IllegalStateException if {@code dir} is a not directory
* @throws IllegalStateException if {@code dir} does not exist
* @throws IllegalStateException if {@code toFile} can not be created
*/
public void zipDir(File dir, File toFile) {
try {
zipDirOrThrowIOE(dir, toFile);
} catch (IOException e) {
throw new IllegalStateException("Can not zip directory " + dir + " to file " + toFile, e);
}
}
/**
* Creates specified directory if it does not exist yet and any non existing parent.
*
* @throws IllegalStateException if specified File exists but is not a directory
* @throws IllegalStateException if directory creation failed
*/
public void createDir(File dir) {
Path dirPath = requireNonNull(dir, "dir can not be null").toPath();
if (dirPath.toFile().exists()) {
checkState(dirPath.toFile().isDirectory(), "%s is not a directory", dirPath);
} else {
try {
createDirectories(dirPath);
} catch (IOException e) {
throw new IllegalStateException(format("Failed to create directory %s", dirPath), e);
}
}
}
private static void checkOrThrowIOE(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) throws IOException {
if (!expression) {
throw new IOException(format(errorMessageTemplate, errorMessageArgs));
}
}
}
|