Mercurial > projects > ldc
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tango/tango/io/vfs/FileFolder.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,808 @@ +/******************************************************************************* + + copyright: Copyright (c) 2007 Kris Bell. All rights reserved + + license: BSD style: $(LICENSE) + + version: Oct 2007: Initial version + + author: Kris + +*******************************************************************************/ + +module tango.io.vfs.FileFolder; + +private import tango.io.FilePath, + tango.io.FileConduit; + +private import tango.util.PathUtil; + +private import tango.core.Exception; + +private import tango.io.vfs.model.Vfs; + +private import tango.io.model.IConduit; + +/******************************************************************************* + + Represents a physical folder in a file system. Use one of these + to address specific paths (sub-trees) within the file system. + +*******************************************************************************/ + +class FileFolder : VfsFolder +{ + private FilePath path; + private VfsStats stats; + + /*********************************************************************** + + Create a file folder with the given name and path. The + name itself should not include '.' or '/' characters, + though the path can point at whatever it pleases. + + Option 'create' will create the folder when set true, + and open an existing folder otherwise + + ***********************************************************************/ + + this (char[] path, bool create=false) + { + this.path = open (FilePath(path), create); + } + + /*********************************************************************** + + create a FileFolder as a Group member + + ***********************************************************************/ + + private this (FilePath path) + { + this.path = path; + } + + /*********************************************************************** + + explicitly create() or open() a named folder + + ***********************************************************************/ + + private this (FileFolder parent, char[] name, bool create=false) + { + assert (parent); + this.path = open (parent.path.dup.append(name), create); + } + + /*********************************************************************** + + Return a short name + + ***********************************************************************/ + + final char[] name () + { + return path.name; + } + + /*********************************************************************** + + Return a long name + + ***********************************************************************/ + + final char[] toString () + { + return path.toString; + } + + /*********************************************************************** + + A folder is being added or removed from the hierarchy. Use + this to test for validity (or whatever) and throw exceptions + as necessary + + Here we test for folder overlap, and bail-out when found. + + ***********************************************************************/ + + final void verify (VfsFolder folder, bool mounting) + { + if (mounting && cast(FileFolder) folder) + { + auto src = FilePath.padded(this.toString); + auto dst = FilePath.padded(folder.toString); + + auto len = src.length; + if (len > dst.length) + len = dst.length; + + if (src[0..len] == dst[0..len]) + error ("folders '"~dst~"' and '"~src~"' overlap"); + } + } + + /*********************************************************************** + + Return a contained file representation + + ***********************************************************************/ + + final VfsFile file (char[] name) + { + return (new FileHost).set (path.toString, name); + } + + /*********************************************************************** + + Return a contained folder representation + + ***********************************************************************/ + + final VfsFolderEntry folder (char[] path) + { + return new FolderHost (this, path); + } + + /*********************************************************************** + + Remove the folder subtree + + ***********************************************************************/ + + final VfsFolder clear () + { + path.remove; + return this; + } + + /*********************************************************************** + + Is folder writable? + + ***********************************************************************/ + + final bool writable () + { + return path.isWritable; + } + + /*********************************************************************** + + Returns content information about this folder + + ***********************************************************************/ + + final VfsFolders self () + { + return new FolderGroup (this, false); + } + + /*********************************************************************** + + Returns a subtree of folders matching the given name + + ***********************************************************************/ + + final VfsFolders tree () + { + return new FolderGroup (this, true); + } + + /*********************************************************************** + + Iterate over the set of immediate child folders. This is + useful for reflecting the hierarchy + + ***********************************************************************/ + + final int opApply (int delegate(inout VfsFolder) dg) + { + int result; + + foreach (folder; folders(true)) + { + VfsFolder x = folder; + if ((result = dg(x)) != 0) + break; + } + return result; + } + + /*********************************************************************** + + Close and/or synchronize changes made to this folder. Each + driver should take advantage of this as appropriate, perhaps + combining multiple files together, or possibly copying to a + remote location + + ***********************************************************************/ + + VfsFolder close (bool commit = true) + { + return this; + } + + /*********************************************************************** + + Sweep owned folders + + ***********************************************************************/ + + private final FileFolder[] folders (bool collect) + { + FileFolder[] folders; + + stats = stats.init; + foreach (info; path) + if (info.folder) + { + if (collect) + folders ~= new FileFolder (FilePath.from (info)); + ++stats.folders; + } + else + { + stats.bytes += info.bytes; + ++stats.files; + } + + return folders; + } + + + /*********************************************************************** + + Sweep owned files + + ***********************************************************************/ + + private final FilePath[] files (ref VfsStats stats, VfsFilter filter = null) + { + FilePath[] files; + + foreach (info; path) + if (info.folder is false) + if (filter is null || filter(cast(VfsInfo) &info)) + { + files ~= FilePath.from (info); + stats.bytes += info.bytes; + ++stats.files; + } + + return files; + } + + /*********************************************************************** + + Throw an exception + + ***********************************************************************/ + + private final char[] error (char[] msg) + { + throw new VfsException (msg); + } + + /*********************************************************************** + + Create or open the given path, and detect path errors + + ***********************************************************************/ + + private final FilePath open (FilePath path, bool create) + { + if (path.exists) + { + if (path.isFolder is false) + error ("FileFolder.open :: path exists but not as a folder: "~path.toString); + } + else + if (create) + path.create; + else + error ("FileFolder.open :: path does not exist: "~path.toString); + return path; + } +} + + +/******************************************************************************* + + Represents a group of files (need this declared here to avoid + a bunch of bizarre compiler warnings) + +*******************************************************************************/ + +class FileGroup : VfsFiles +{ + private FilePath[] group; + private VfsStats stats; + + /*********************************************************************** + + ***********************************************************************/ + + this (FolderGroup host, VfsFilter filter) + { + foreach (folder; host.members) + group ~= folder.files (stats, filter); + } + + /*********************************************************************** + + Iterate over the set of contained VfsFile instances + + ***********************************************************************/ + + final int opApply (int delegate(inout VfsFile) dg) + { + int result; + auto host = new FileHost; + + foreach (file; group) + { + host.path = file; + VfsFile x = host; + if ((result = dg(x)) != 0) + break; + } + return result; + } + + /*********************************************************************** + + Return the total number of entries + + ***********************************************************************/ + + final uint files () + { + return group.length; + } + + /*********************************************************************** + + Return the total size of all files + + ***********************************************************************/ + + final ulong bytes () + { + return stats.bytes; + } +} + + +/******************************************************************************* + + A set of folders representing a selection. This is where file + selection is made, and pattern-matched folder subsets can be + extracted. You need one of these to expose statistics (such as + file or folder count) of a selected folder group + +*******************************************************************************/ + +private class FolderGroup : VfsFolders +{ + private FileFolder[] members; // folders in group + + /*********************************************************************** + + Create a subset group + + ***********************************************************************/ + + private this () {} + + /*********************************************************************** + + Create a folder group including the provided folder and + (optionally) all child folders + + ***********************************************************************/ + + private this (FileFolder root, bool recurse) + { + members = root ~ scan (root, recurse); + } + + /*********************************************************************** + + Iterate over the set of contained VfsFolder instances + + ***********************************************************************/ + + final int opApply (int delegate(inout VfsFolder) dg) + { + int result; + + foreach (folder; members) + { + VfsFolder x = folder; + if ((result = dg(x)) != 0) + break; + } + return result; + } + + /*********************************************************************** + + Return the number of files in this group + + ***********************************************************************/ + + final uint files () + { + uint files; + foreach (folder; members) + files += folder.stats.files; + return files; + } + + /*********************************************************************** + + Return the total size of all files in this group + + ***********************************************************************/ + + final ulong bytes () + { + ulong bytes; + + foreach (folder; members) + bytes += folder.stats.bytes; + return bytes; + } + + /*********************************************************************** + + Return the number of folders in this group + + ***********************************************************************/ + + final uint folders () + { + return members.length; + } + + /*********************************************************************** + + Return the total number of entries in this group + + ***********************************************************************/ + + final uint entries () + { + return files + folders; + } + + /*********************************************************************** + + Return a subset of folders matching the given pattern + + ***********************************************************************/ + + final VfsFolders subset (char[] pattern) + { + auto set = new FolderGroup; + + foreach (folder; members) + if (patternMatch (folder.path.name, pattern)) + set.members ~= folder; + return set; + } + + /*********************************************************************** + + Return a set of files matching the given pattern + + ***********************************************************************/ + + final FileGroup catalog (char[] pattern) + { + bool foo (VfsInfo info) + { + return patternMatch (info.name, pattern); + } + + return catalog (&foo); + } + + /*********************************************************************** + + Returns a set of files conforming to the given filter + + ***********************************************************************/ + + final FileGroup catalog (VfsFilter filter = null) + { + return new FileGroup (this, filter); + } + + /*********************************************************************** + + Internal routine to traverse the folder tree + + ***********************************************************************/ + + private final FileFolder[] scan (FileFolder root, bool recurse) + { + auto folders = root.folders (recurse); + if (recurse) + foreach (child; folders) + folders ~= scan (child, recurse); + return folders; + } +} + + +/******************************************************************************* + + A host for folders, currently used to harbor create() and open() + methods only + +*******************************************************************************/ + +private class FolderHost : VfsFolderEntry +{ + private char[] path; + private FileFolder parent; + + /*********************************************************************** + + ***********************************************************************/ + + private this (FileFolder parent, char[] path) + { + this.path = path; + this.parent = parent; + } + + /*********************************************************************** + + ***********************************************************************/ + + final VfsFolder create () + { + return new FileFolder (parent, path, true); + } + + /*********************************************************************** + + ***********************************************************************/ + + final VfsFolder open () + { + return new FileFolder (parent, path, false); + } + + /*********************************************************************** + + Test to see if a folder exists + + ***********************************************************************/ + + bool exists () + { + try { + open(); + return true; + } catch (IOException x) {} + return false; + } +} + + +/******************************************************************************* + + Represents things you can do with a file + +*******************************************************************************/ + +private class FileHost : VfsFile +{ + private FilePath path; + + /*********************************************************************** + + ***********************************************************************/ + + private this (char[] path = null) + { + this.path = FilePath (path); + } + + /*********************************************************************** + + Return a short name + + ***********************************************************************/ + + final char[] name() + { + return path.file; + } + + /*********************************************************************** + + Return a long name + + ***********************************************************************/ + + final char[] toString () + { + return path.toString; + } + + /*********************************************************************** + + Does this file exist? + + ***********************************************************************/ + + final bool exists() + { + return path.exists; + } + + /*********************************************************************** + + Return the file size + + ***********************************************************************/ + + final ulong size() + { + return path.fileSize; + } + + /*********************************************************************** + + Create a new file instance + + ***********************************************************************/ + + final VfsFile create () + { + path.createFile (); + return this; + } + + /*********************************************************************** + + Create a new file instance and populate with stream + + ***********************************************************************/ + + final VfsFile create (InputStream input) + { + create.output.copy(input).close; + return this; + } + + /*********************************************************************** + + Create and copy the given source + + ***********************************************************************/ + + VfsFile copy (VfsFile source) + { + auto input = source.input; + scope (exit) input.close; + return create (input); + } + + /*********************************************************************** + + Create and copy the given source, and remove the source + + ***********************************************************************/ + + final VfsFile move (VfsFile source) + { + copy (source); + source.remove; + return this; + } + + /*********************************************************************** + + Return the input stream. Don't forget to close it + + ***********************************************************************/ + + final InputStream input () + { + return new FileConduit (path.dup); + } + + /*********************************************************************** + + Return the output stream. Don't forget to close it + + ***********************************************************************/ + + final OutputStream output () + { + return new FileConduit (path.dup, FileConduit.WriteExisting); + } + + /*********************************************************************** + + Remove this file + + ***********************************************************************/ + + final VfsFile remove () + { + path.remove; + return this; + } + + /*********************************************************************** + + Duplicate this entry + + ***********************************************************************/ + + final VfsFile dup() + { + return new FileHost (path.toString); + } + + /*********************************************************************** + + ***********************************************************************/ + + private VfsFile set (char[] folder, char[] name) + { + path.set(folder).append(name); + return this; + } +} + + +debug (FileFolder) +{ + +/******************************************************************************* + +*******************************************************************************/ + +import tango.io.Stdout; +import tango.io.Buffer; + +void main() +{ + auto root = new FileFolder ("d:/d/import/temp", true); + root.folder("test").create; + root.file("test.txt").create(new Buffer("hello")); + Stdout.formatln ("test.txt.length = {}", root.file("test.txt").size); + + root = new FileFolder ("c:/"); + + auto set = root.self; + + Stdout.formatln ("self.files = {}", set.files); + Stdout.formatln ("self.bytes = {}", set.bytes); + Stdout.formatln ("self.folders = {}", set.folders); + Stdout.formatln ("self.entries = {}", set.entries); + + set = root.tree; + Stdout.formatln ("tree.files = {}", set.files); + Stdout.formatln ("tree.bytes = {}", set.bytes); + Stdout.formatln ("tree.folders = {}", set.folders); + Stdout.formatln ("tree.entries = {}", set.entries); + + //foreach (folder; set) + // Stdout.formatln ("tree.folder '{}' has {} files", folder.name, folder.self.files); + + auto cat = set.catalog ("s*"); + Stdout.formatln ("cat.files = {}", cat.files); + Stdout.formatln ("cat.bytes = {}", cat.bytes); + //foreach (file; cat) + // Stdout.formatln ("cat.name '{}' '{}'", file.name, file.toString); +} +}