1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package net.grinder.util;
23
24 import java.io.File;
25 import java.io.FileFilter;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.Serializable;
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34
35 import net.grinder.common.Closer;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class Directory implements Serializable {
51 private static final long serialVersionUID = 1;
52
53 private static final FileFilter s_matchAllFilesFilter =
54 new MatchAllFilesFilter();
55
56 private final File m_directory;
57 private final List<String> m_warnings = new ArrayList<String>();
58
59
60
61
62
63
64 public static FileFilter getMatchAllFilesFilter() {
65 return s_matchAllFilesFilter;
66 }
67
68
69
70
71 public Directory() {
72 m_directory = new File(".");
73 }
74
75
76
77
78
79
80
81
82
83 public Directory(final File directory) throws DirectoryException {
84 if (directory == null) {
85 m_directory = new File(".");
86 }
87 else {
88 if (directory.exists() && !directory.isDirectory()) {
89 throw new DirectoryException(
90 "'" + directory.getPath() + "' is not a directory");
91 }
92
93 m_directory = directory;
94 }
95 }
96
97
98
99
100
101
102 public void create() throws DirectoryException {
103 if (!getFile().exists()) {
104 if (!getFile().mkdirs()) {
105 throw new DirectoryException(
106 "Could not create directory '" + getFile() + "'");
107 }
108 }
109 }
110
111
112
113
114
115
116 public File getFile() {
117 return m_directory;
118 }
119
120
121
122
123
124
125
126
127
128
129 public File getFile(final File child) {
130 if (child == null) {
131 return getFile();
132 }
133 else {
134 return new File(getFile(), child.getPath());
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147
148 public File[] listContents(final FileFilter filter) {
149 return listContents(filter, false, false);
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public File[] listContents(final FileFilter filter,
168 final boolean includeDirectories,
169 final boolean absolutePaths) {
170
171 final List<File> resultList = new ArrayList<File>();
172 final Set<File> visited = new HashSet<File>();
173 final List<File> directoriesToVisit = new ArrayList<File>();
174
175 final File rootFile = getFile();
176
177 if (rootFile.exists() && filter.accept(rootFile)) {
178
179
180
181 directoriesToVisit.add(null);
182
183 if (includeDirectories) {
184 resultList.add(absolutePaths ? rootFile : new File(""));
185 }
186 }
187
188 while (directoriesToVisit.size() > 0) {
189 final File[] directories =
190 directoriesToVisit.toArray(new File[directoriesToVisit.size()]);
191
192 directoriesToVisit.clear();
193
194 for (final File relativeDirectory : directories) {
195 final File absoluteDirectory = getFile(relativeDirectory);
196
197 visited.add(relativeDirectory);
198
199
200
201 final String[] children = absoluteDirectory.list();
202
203 if (children == null) {
204
205
206 synchronized (m_warnings) {
207 m_warnings.add("Could not list '" + absoluteDirectory);
208 }
209 continue;
210 }
211
212 for (final String element : children) {
213 final File relativeChild = new File(relativeDirectory, element);
214 final File absoluteChild = new File(absoluteDirectory, element);
215
216 if (filter.accept(absoluteChild)) {
217
218
219
220 if (includeDirectories && absoluteChild.isDirectory() ||
221 absoluteChild.isFile()) {
222 resultList.add(absolutePaths ? absoluteChild : relativeChild);
223 }
224
225 if (absoluteChild.isDirectory() &&
226 !visited.contains(relativeChild)) {
227 directoriesToVisit.add(relativeChild);
228 }
229 }
230 }
231 }
232 }
233
234 return resultList.toArray(new File[resultList.size()]);
235 }
236
237
238
239
240
241
242
243
244
245
246 public void deleteContents() throws DirectoryException {
247
248
249 final File[] deleteList = listContents(s_matchAllFilesFilter, true, true);
250
251 for (int i = deleteList.length - 1; i >= 0; --i) {
252 if (deleteList[i].equals(getFile())) {
253 continue;
254 }
255
256 if (!deleteList[i].delete()) {
257 throw new DirectoryException(
258 "Could not delete '" + deleteList[i] + "'");
259 }
260 }
261 }
262
263
264
265
266
267
268
269
270 public void delete() throws DirectoryException {
271 if (!getFile().delete()) {
272 throw new DirectoryException("Could not delete '" + getFile() + "'");
273 }
274 }
275
276
277
278
279
280
281
282
283
284 public File rebaseFromCWD(final File file) {
285 final File absolute = file.getAbsoluteFile();
286 final File relativeResult = relativeFile(absolute, true);
287
288 if (relativeResult == null) {
289 return absolute;
290 }
291
292 return relativeResult;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 public File relativeFile(final File file, final boolean mustBeChild) {
313
314 final File f;
315
316 if (file.isAbsolute()) {
317 f = file;
318 }
319 else if (!mustBeChild) {
320 return file;
321 }
322 else {
323 f = getFile(file);
324 }
325
326 return relativePath(getFile(), f, mustBeChild);
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349 static File relativePath(final File from,
350 final File to,
351 final boolean mustBeChild) {
352 final String[] fromPaths;
353 final String[] toPaths;
354
355 try {
356 fromPaths = splitPath(from.getCanonicalPath());
357 toPaths = splitPath(to.getCanonicalFile().getPath());
358 }
359 catch (final IOException e) {
360 throw new UnexpectedIOException(e);
361 }
362
363 int i = 0;
364
365 while (i < fromPaths.length &&
366 i < toPaths.length &&
367 fromPaths[i].equals(toPaths[i])) {
368 ++i;
369 }
370
371 if (mustBeChild && i != fromPaths.length) {
372 return null;
373 }
374
375
376
377 if (i <= 1) {
378 return null;
379 }
380
381 final StringBuilder result = new StringBuilder();
382
383 for (int j = i; j < fromPaths.length; ++j) {
384 result.append("..");
385 result.append(File.separator);
386 }
387
388 for (int j = i; j < toPaths.length; ++j) {
389 result.append(toPaths[j]);
390
391 if (j != toPaths.length - 1) {
392 result.append(File.separator);
393 }
394 }
395
396 if (result.length() == 0) {
397 return new File(".");
398 }
399
400 return new File(result.toString());
401 }
402
403 private static String[] splitPath(final String path) {
404 return path.split(File.separatorChar == '\\' ? "\\\\" : File.separator);
405 }
406
407
408
409
410
411
412
413
414
415 public List<File> rebasePath(final String path) {
416 final String[] elements = path.split(File.pathSeparator);
417
418 final List<File> result = new ArrayList<File>(elements.length);
419
420 for (final String e : elements) {
421 if (!e.isEmpty()) {
422 result.add(rebaseFromCWD(new File(e)));
423 }
424 }
425
426 return result;
427 }
428
429
430
431
432
433
434
435
436 public boolean isParentOf(final File file) {
437 final File thisFile = getFile();
438
439 File candidate = file.getParentFile();
440
441 while (candidate != null) {
442 if (thisFile.equals(candidate)) {
443 return true;
444 }
445
446 candidate = candidate.getParentFile();
447 }
448
449
450 return false;
451 }
452
453
454
455
456
457
458
459
460
461
462 public void copyTo(final Directory target, final boolean incremental)
463 throws IOException {
464
465 if (!getFile().exists()) {
466 throw new DirectoryException(
467 "Source directory '" + getFile() + "' does not exist");
468 }
469
470 target.create();
471
472 if (!incremental) {
473 target.deleteContents();
474 }
475
476 final File[] files = listContents(s_matchAllFilesFilter, true, false);
477 final StreamCopier streamCopier = new StreamCopier(4096, false);
478
479 for (final File file : files) {
480 final File source = getFile(file);
481 final File destination = target.getFile(file);
482
483 if (source.isDirectory()) {
484 destination.mkdirs();
485 }
486 else {
487
488 if (!incremental ||
489 !destination.exists() ||
490 source.lastModified() > destination.lastModified()) {
491
492 FileInputStream in = null;
493 FileOutputStream out = null;
494
495 try {
496 in = new FileInputStream(source);
497 out = new FileOutputStream(destination);
498 streamCopier.copy(in, out);
499 }
500 finally {
501 Closer.close(in);
502 Closer.close(out);
503 }
504 }
505 }
506 }
507 }
508
509
510
511
512
513
514
515 public String[] getWarnings() {
516 synchronized (m_warnings) {
517 try {
518 return m_warnings.toArray(new String[m_warnings.size()]);
519 }
520 finally {
521 m_warnings.clear();
522 }
523 }
524 }
525
526
527
528
529
530 public static final class DirectoryException extends IOException {
531 DirectoryException(final String message) {
532 super(message);
533 }
534 }
535
536
537
538
539
540
541 @Override
542 public int hashCode() {
543 return getFile().hashCode();
544 }
545
546
547
548
549
550
551
552 @Override
553 public boolean equals(final Object o) {
554 if (o == this) {
555 return true;
556 }
557
558 if (o == null || o.getClass() != Directory.class) {
559 return false;
560 }
561
562 return getFile().equals(((Directory)o).getFile());
563 }
564
565 private static class MatchAllFilesFilter implements FileFilter {
566 @Override
567 public boolean accept(final File file) {
568 return true;
569 }
570 }
571 }