Mercurial > projects > ldc
comparison tango/tango/io/vfs/FileFolder.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /******************************************************************************* | |
2 | |
3 copyright: Copyright (c) 2007 Kris Bell. All rights reserved | |
4 | |
5 license: BSD style: $(LICENSE) | |
6 | |
7 version: Oct 2007: Initial version | |
8 | |
9 author: Kris | |
10 | |
11 *******************************************************************************/ | |
12 | |
13 module tango.io.vfs.FileFolder; | |
14 | |
15 private import tango.io.FilePath, | |
16 tango.io.FileConduit; | |
17 | |
18 private import tango.util.PathUtil; | |
19 | |
20 private import tango.core.Exception; | |
21 | |
22 private import tango.io.vfs.model.Vfs; | |
23 | |
24 private import tango.io.model.IConduit; | |
25 | |
26 /******************************************************************************* | |
27 | |
28 Represents a physical folder in a file system. Use one of these | |
29 to address specific paths (sub-trees) within the file system. | |
30 | |
31 *******************************************************************************/ | |
32 | |
33 class FileFolder : VfsFolder | |
34 { | |
35 private FilePath path; | |
36 private VfsStats stats; | |
37 | |
38 /*********************************************************************** | |
39 | |
40 Create a file folder with the given name and path. The | |
41 name itself should not include '.' or '/' characters, | |
42 though the path can point at whatever it pleases. | |
43 | |
44 Option 'create' will create the folder when set true, | |
45 and open an existing folder otherwise | |
46 | |
47 ***********************************************************************/ | |
48 | |
49 this (char[] path, bool create=false) | |
50 { | |
51 this.path = open (FilePath(path), create); | |
52 } | |
53 | |
54 /*********************************************************************** | |
55 | |
56 create a FileFolder as a Group member | |
57 | |
58 ***********************************************************************/ | |
59 | |
60 private this (FilePath path) | |
61 { | |
62 this.path = path; | |
63 } | |
64 | |
65 /*********************************************************************** | |
66 | |
67 explicitly create() or open() a named folder | |
68 | |
69 ***********************************************************************/ | |
70 | |
71 private this (FileFolder parent, char[] name, bool create=false) | |
72 { | |
73 assert (parent); | |
74 this.path = open (parent.path.dup.append(name), create); | |
75 } | |
76 | |
77 /*********************************************************************** | |
78 | |
79 Return a short name | |
80 | |
81 ***********************************************************************/ | |
82 | |
83 final char[] name () | |
84 { | |
85 return path.name; | |
86 } | |
87 | |
88 /*********************************************************************** | |
89 | |
90 Return a long name | |
91 | |
92 ***********************************************************************/ | |
93 | |
94 final char[] toString () | |
95 { | |
96 return path.toString; | |
97 } | |
98 | |
99 /*********************************************************************** | |
100 | |
101 A folder is being added or removed from the hierarchy. Use | |
102 this to test for validity (or whatever) and throw exceptions | |
103 as necessary | |
104 | |
105 Here we test for folder overlap, and bail-out when found. | |
106 | |
107 ***********************************************************************/ | |
108 | |
109 final void verify (VfsFolder folder, bool mounting) | |
110 { | |
111 if (mounting && cast(FileFolder) folder) | |
112 { | |
113 auto src = FilePath.padded(this.toString); | |
114 auto dst = FilePath.padded(folder.toString); | |
115 | |
116 auto len = src.length; | |
117 if (len > dst.length) | |
118 len = dst.length; | |
119 | |
120 if (src[0..len] == dst[0..len]) | |
121 error ("folders '"~dst~"' and '"~src~"' overlap"); | |
122 } | |
123 } | |
124 | |
125 /*********************************************************************** | |
126 | |
127 Return a contained file representation | |
128 | |
129 ***********************************************************************/ | |
130 | |
131 final VfsFile file (char[] name) | |
132 { | |
133 return (new FileHost).set (path.toString, name); | |
134 } | |
135 | |
136 /*********************************************************************** | |
137 | |
138 Return a contained folder representation | |
139 | |
140 ***********************************************************************/ | |
141 | |
142 final VfsFolderEntry folder (char[] path) | |
143 { | |
144 return new FolderHost (this, path); | |
145 } | |
146 | |
147 /*********************************************************************** | |
148 | |
149 Remove the folder subtree | |
150 | |
151 ***********************************************************************/ | |
152 | |
153 final VfsFolder clear () | |
154 { | |
155 path.remove; | |
156 return this; | |
157 } | |
158 | |
159 /*********************************************************************** | |
160 | |
161 Is folder writable? | |
162 | |
163 ***********************************************************************/ | |
164 | |
165 final bool writable () | |
166 { | |
167 return path.isWritable; | |
168 } | |
169 | |
170 /*********************************************************************** | |
171 | |
172 Returns content information about this folder | |
173 | |
174 ***********************************************************************/ | |
175 | |
176 final VfsFolders self () | |
177 { | |
178 return new FolderGroup (this, false); | |
179 } | |
180 | |
181 /*********************************************************************** | |
182 | |
183 Returns a subtree of folders matching the given name | |
184 | |
185 ***********************************************************************/ | |
186 | |
187 final VfsFolders tree () | |
188 { | |
189 return new FolderGroup (this, true); | |
190 } | |
191 | |
192 /*********************************************************************** | |
193 | |
194 Iterate over the set of immediate child folders. This is | |
195 useful for reflecting the hierarchy | |
196 | |
197 ***********************************************************************/ | |
198 | |
199 final int opApply (int delegate(inout VfsFolder) dg) | |
200 { | |
201 int result; | |
202 | |
203 foreach (folder; folders(true)) | |
204 { | |
205 VfsFolder x = folder; | |
206 if ((result = dg(x)) != 0) | |
207 break; | |
208 } | |
209 return result; | |
210 } | |
211 | |
212 /*********************************************************************** | |
213 | |
214 Close and/or synchronize changes made to this folder. Each | |
215 driver should take advantage of this as appropriate, perhaps | |
216 combining multiple files together, or possibly copying to a | |
217 remote location | |
218 | |
219 ***********************************************************************/ | |
220 | |
221 VfsFolder close (bool commit = true) | |
222 { | |
223 return this; | |
224 } | |
225 | |
226 /*********************************************************************** | |
227 | |
228 Sweep owned folders | |
229 | |
230 ***********************************************************************/ | |
231 | |
232 private final FileFolder[] folders (bool collect) | |
233 { | |
234 FileFolder[] folders; | |
235 | |
236 stats = stats.init; | |
237 foreach (info; path) | |
238 if (info.folder) | |
239 { | |
240 if (collect) | |
241 folders ~= new FileFolder (FilePath.from (info)); | |
242 ++stats.folders; | |
243 } | |
244 else | |
245 { | |
246 stats.bytes += info.bytes; | |
247 ++stats.files; | |
248 } | |
249 | |
250 return folders; | |
251 } | |
252 | |
253 | |
254 /*********************************************************************** | |
255 | |
256 Sweep owned files | |
257 | |
258 ***********************************************************************/ | |
259 | |
260 private final FilePath[] files (ref VfsStats stats, VfsFilter filter = null) | |
261 { | |
262 FilePath[] files; | |
263 | |
264 foreach (info; path) | |
265 if (info.folder is false) | |
266 if (filter is null || filter(cast(VfsInfo) &info)) | |
267 { | |
268 files ~= FilePath.from (info); | |
269 stats.bytes += info.bytes; | |
270 ++stats.files; | |
271 } | |
272 | |
273 return files; | |
274 } | |
275 | |
276 /*********************************************************************** | |
277 | |
278 Throw an exception | |
279 | |
280 ***********************************************************************/ | |
281 | |
282 private final char[] error (char[] msg) | |
283 { | |
284 throw new VfsException (msg); | |
285 } | |
286 | |
287 /*********************************************************************** | |
288 | |
289 Create or open the given path, and detect path errors | |
290 | |
291 ***********************************************************************/ | |
292 | |
293 private final FilePath open (FilePath path, bool create) | |
294 { | |
295 if (path.exists) | |
296 { | |
297 if (path.isFolder is false) | |
298 error ("FileFolder.open :: path exists but not as a folder: "~path.toString); | |
299 } | |
300 else | |
301 if (create) | |
302 path.create; | |
303 else | |
304 error ("FileFolder.open :: path does not exist: "~path.toString); | |
305 return path; | |
306 } | |
307 } | |
308 | |
309 | |
310 /******************************************************************************* | |
311 | |
312 Represents a group of files (need this declared here to avoid | |
313 a bunch of bizarre compiler warnings) | |
314 | |
315 *******************************************************************************/ | |
316 | |
317 class FileGroup : VfsFiles | |
318 { | |
319 private FilePath[] group; | |
320 private VfsStats stats; | |
321 | |
322 /*********************************************************************** | |
323 | |
324 ***********************************************************************/ | |
325 | |
326 this (FolderGroup host, VfsFilter filter) | |
327 { | |
328 foreach (folder; host.members) | |
329 group ~= folder.files (stats, filter); | |
330 } | |
331 | |
332 /*********************************************************************** | |
333 | |
334 Iterate over the set of contained VfsFile instances | |
335 | |
336 ***********************************************************************/ | |
337 | |
338 final int opApply (int delegate(inout VfsFile) dg) | |
339 { | |
340 int result; | |
341 auto host = new FileHost; | |
342 | |
343 foreach (file; group) | |
344 { | |
345 host.path = file; | |
346 VfsFile x = host; | |
347 if ((result = dg(x)) != 0) | |
348 break; | |
349 } | |
350 return result; | |
351 } | |
352 | |
353 /*********************************************************************** | |
354 | |
355 Return the total number of entries | |
356 | |
357 ***********************************************************************/ | |
358 | |
359 final uint files () | |
360 { | |
361 return group.length; | |
362 } | |
363 | |
364 /*********************************************************************** | |
365 | |
366 Return the total size of all files | |
367 | |
368 ***********************************************************************/ | |
369 | |
370 final ulong bytes () | |
371 { | |
372 return stats.bytes; | |
373 } | |
374 } | |
375 | |
376 | |
377 /******************************************************************************* | |
378 | |
379 A set of folders representing a selection. This is where file | |
380 selection is made, and pattern-matched folder subsets can be | |
381 extracted. You need one of these to expose statistics (such as | |
382 file or folder count) of a selected folder group | |
383 | |
384 *******************************************************************************/ | |
385 | |
386 private class FolderGroup : VfsFolders | |
387 { | |
388 private FileFolder[] members; // folders in group | |
389 | |
390 /*********************************************************************** | |
391 | |
392 Create a subset group | |
393 | |
394 ***********************************************************************/ | |
395 | |
396 private this () {} | |
397 | |
398 /*********************************************************************** | |
399 | |
400 Create a folder group including the provided folder and | |
401 (optionally) all child folders | |
402 | |
403 ***********************************************************************/ | |
404 | |
405 private this (FileFolder root, bool recurse) | |
406 { | |
407 members = root ~ scan (root, recurse); | |
408 } | |
409 | |
410 /*********************************************************************** | |
411 | |
412 Iterate over the set of contained VfsFolder instances | |
413 | |
414 ***********************************************************************/ | |
415 | |
416 final int opApply (int delegate(inout VfsFolder) dg) | |
417 { | |
418 int result; | |
419 | |
420 foreach (folder; members) | |
421 { | |
422 VfsFolder x = folder; | |
423 if ((result = dg(x)) != 0) | |
424 break; | |
425 } | |
426 return result; | |
427 } | |
428 | |
429 /*********************************************************************** | |
430 | |
431 Return the number of files in this group | |
432 | |
433 ***********************************************************************/ | |
434 | |
435 final uint files () | |
436 { | |
437 uint files; | |
438 foreach (folder; members) | |
439 files += folder.stats.files; | |
440 return files; | |
441 } | |
442 | |
443 /*********************************************************************** | |
444 | |
445 Return the total size of all files in this group | |
446 | |
447 ***********************************************************************/ | |
448 | |
449 final ulong bytes () | |
450 { | |
451 ulong bytes; | |
452 | |
453 foreach (folder; members) | |
454 bytes += folder.stats.bytes; | |
455 return bytes; | |
456 } | |
457 | |
458 /*********************************************************************** | |
459 | |
460 Return the number of folders in this group | |
461 | |
462 ***********************************************************************/ | |
463 | |
464 final uint folders () | |
465 { | |
466 return members.length; | |
467 } | |
468 | |
469 /*********************************************************************** | |
470 | |
471 Return the total number of entries in this group | |
472 | |
473 ***********************************************************************/ | |
474 | |
475 final uint entries () | |
476 { | |
477 return files + folders; | |
478 } | |
479 | |
480 /*********************************************************************** | |
481 | |
482 Return a subset of folders matching the given pattern | |
483 | |
484 ***********************************************************************/ | |
485 | |
486 final VfsFolders subset (char[] pattern) | |
487 { | |
488 auto set = new FolderGroup; | |
489 | |
490 foreach (folder; members) | |
491 if (patternMatch (folder.path.name, pattern)) | |
492 set.members ~= folder; | |
493 return set; | |
494 } | |
495 | |
496 /*********************************************************************** | |
497 | |
498 Return a set of files matching the given pattern | |
499 | |
500 ***********************************************************************/ | |
501 | |
502 final FileGroup catalog (char[] pattern) | |
503 { | |
504 bool foo (VfsInfo info) | |
505 { | |
506 return patternMatch (info.name, pattern); | |
507 } | |
508 | |
509 return catalog (&foo); | |
510 } | |
511 | |
512 /*********************************************************************** | |
513 | |
514 Returns a set of files conforming to the given filter | |
515 | |
516 ***********************************************************************/ | |
517 | |
518 final FileGroup catalog (VfsFilter filter = null) | |
519 { | |
520 return new FileGroup (this, filter); | |
521 } | |
522 | |
523 /*********************************************************************** | |
524 | |
525 Internal routine to traverse the folder tree | |
526 | |
527 ***********************************************************************/ | |
528 | |
529 private final FileFolder[] scan (FileFolder root, bool recurse) | |
530 { | |
531 auto folders = root.folders (recurse); | |
532 if (recurse) | |
533 foreach (child; folders) | |
534 folders ~= scan (child, recurse); | |
535 return folders; | |
536 } | |
537 } | |
538 | |
539 | |
540 /******************************************************************************* | |
541 | |
542 A host for folders, currently used to harbor create() and open() | |
543 methods only | |
544 | |
545 *******************************************************************************/ | |
546 | |
547 private class FolderHost : VfsFolderEntry | |
548 { | |
549 private char[] path; | |
550 private FileFolder parent; | |
551 | |
552 /*********************************************************************** | |
553 | |
554 ***********************************************************************/ | |
555 | |
556 private this (FileFolder parent, char[] path) | |
557 { | |
558 this.path = path; | |
559 this.parent = parent; | |
560 } | |
561 | |
562 /*********************************************************************** | |
563 | |
564 ***********************************************************************/ | |
565 | |
566 final VfsFolder create () | |
567 { | |
568 return new FileFolder (parent, path, true); | |
569 } | |
570 | |
571 /*********************************************************************** | |
572 | |
573 ***********************************************************************/ | |
574 | |
575 final VfsFolder open () | |
576 { | |
577 return new FileFolder (parent, path, false); | |
578 } | |
579 | |
580 /*********************************************************************** | |
581 | |
582 Test to see if a folder exists | |
583 | |
584 ***********************************************************************/ | |
585 | |
586 bool exists () | |
587 { | |
588 try { | |
589 open(); | |
590 return true; | |
591 } catch (IOException x) {} | |
592 return false; | |
593 } | |
594 } | |
595 | |
596 | |
597 /******************************************************************************* | |
598 | |
599 Represents things you can do with a file | |
600 | |
601 *******************************************************************************/ | |
602 | |
603 private class FileHost : VfsFile | |
604 { | |
605 private FilePath path; | |
606 | |
607 /*********************************************************************** | |
608 | |
609 ***********************************************************************/ | |
610 | |
611 private this (char[] path = null) | |
612 { | |
613 this.path = FilePath (path); | |
614 } | |
615 | |
616 /*********************************************************************** | |
617 | |
618 Return a short name | |
619 | |
620 ***********************************************************************/ | |
621 | |
622 final char[] name() | |
623 { | |
624 return path.file; | |
625 } | |
626 | |
627 /*********************************************************************** | |
628 | |
629 Return a long name | |
630 | |
631 ***********************************************************************/ | |
632 | |
633 final char[] toString () | |
634 { | |
635 return path.toString; | |
636 } | |
637 | |
638 /*********************************************************************** | |
639 | |
640 Does this file exist? | |
641 | |
642 ***********************************************************************/ | |
643 | |
644 final bool exists() | |
645 { | |
646 return path.exists; | |
647 } | |
648 | |
649 /*********************************************************************** | |
650 | |
651 Return the file size | |
652 | |
653 ***********************************************************************/ | |
654 | |
655 final ulong size() | |
656 { | |
657 return path.fileSize; | |
658 } | |
659 | |
660 /*********************************************************************** | |
661 | |
662 Create a new file instance | |
663 | |
664 ***********************************************************************/ | |
665 | |
666 final VfsFile create () | |
667 { | |
668 path.createFile (); | |
669 return this; | |
670 } | |
671 | |
672 /*********************************************************************** | |
673 | |
674 Create a new file instance and populate with stream | |
675 | |
676 ***********************************************************************/ | |
677 | |
678 final VfsFile create (InputStream input) | |
679 { | |
680 create.output.copy(input).close; | |
681 return this; | |
682 } | |
683 | |
684 /*********************************************************************** | |
685 | |
686 Create and copy the given source | |
687 | |
688 ***********************************************************************/ | |
689 | |
690 VfsFile copy (VfsFile source) | |
691 { | |
692 auto input = source.input; | |
693 scope (exit) input.close; | |
694 return create (input); | |
695 } | |
696 | |
697 /*********************************************************************** | |
698 | |
699 Create and copy the given source, and remove the source | |
700 | |
701 ***********************************************************************/ | |
702 | |
703 final VfsFile move (VfsFile source) | |
704 { | |
705 copy (source); | |
706 source.remove; | |
707 return this; | |
708 } | |
709 | |
710 /*********************************************************************** | |
711 | |
712 Return the input stream. Don't forget to close it | |
713 | |
714 ***********************************************************************/ | |
715 | |
716 final InputStream input () | |
717 { | |
718 return new FileConduit (path.dup); | |
719 } | |
720 | |
721 /*********************************************************************** | |
722 | |
723 Return the output stream. Don't forget to close it | |
724 | |
725 ***********************************************************************/ | |
726 | |
727 final OutputStream output () | |
728 { | |
729 return new FileConduit (path.dup, FileConduit.WriteExisting); | |
730 } | |
731 | |
732 /*********************************************************************** | |
733 | |
734 Remove this file | |
735 | |
736 ***********************************************************************/ | |
737 | |
738 final VfsFile remove () | |
739 { | |
740 path.remove; | |
741 return this; | |
742 } | |
743 | |
744 /*********************************************************************** | |
745 | |
746 Duplicate this entry | |
747 | |
748 ***********************************************************************/ | |
749 | |
750 final VfsFile dup() | |
751 { | |
752 return new FileHost (path.toString); | |
753 } | |
754 | |
755 /*********************************************************************** | |
756 | |
757 ***********************************************************************/ | |
758 | |
759 private VfsFile set (char[] folder, char[] name) | |
760 { | |
761 path.set(folder).append(name); | |
762 return this; | |
763 } | |
764 } | |
765 | |
766 | |
767 debug (FileFolder) | |
768 { | |
769 | |
770 /******************************************************************************* | |
771 | |
772 *******************************************************************************/ | |
773 | |
774 import tango.io.Stdout; | |
775 import tango.io.Buffer; | |
776 | |
777 void main() | |
778 { | |
779 auto root = new FileFolder ("d:/d/import/temp", true); | |
780 root.folder("test").create; | |
781 root.file("test.txt").create(new Buffer("hello")); | |
782 Stdout.formatln ("test.txt.length = {}", root.file("test.txt").size); | |
783 | |
784 root = new FileFolder ("c:/"); | |
785 | |
786 auto set = root.self; | |
787 | |
788 Stdout.formatln ("self.files = {}", set.files); | |
789 Stdout.formatln ("self.bytes = {}", set.bytes); | |
790 Stdout.formatln ("self.folders = {}", set.folders); | |
791 Stdout.formatln ("self.entries = {}", set.entries); | |
792 | |
793 set = root.tree; | |
794 Stdout.formatln ("tree.files = {}", set.files); | |
795 Stdout.formatln ("tree.bytes = {}", set.bytes); | |
796 Stdout.formatln ("tree.folders = {}", set.folders); | |
797 Stdout.formatln ("tree.entries = {}", set.entries); | |
798 | |
799 //foreach (folder; set) | |
800 // Stdout.formatln ("tree.folder '{}' has {} files", folder.name, folder.self.files); | |
801 | |
802 auto cat = set.catalog ("s*"); | |
803 Stdout.formatln ("cat.files = {}", cat.files); | |
804 Stdout.formatln ("cat.bytes = {}", cat.bytes); | |
805 //foreach (file; cat) | |
806 // Stdout.formatln ("cat.name '{}' '{}'", file.name, file.toString); | |
807 } | |
808 } |