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 }