Mercurial > projects > ldc
diff tango/tango/io/vfs/VirtualFolder.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/VirtualFolder.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,633 @@ +/******************************************************************************* + + copyright: Copyright (c) 2007 Kris Bell. All rights reserved + + license: BSD style: $(LICENSE) + + version: Oct 2007: Initial version + + author: Kris + +*******************************************************************************/ + +module tango.io.vfs.VirtualFolder; + +private import tango.io.FileConst; + +private import tango.util.PathUtil; + +private import tango.core.Exception; + +private import tango.io.vfs.model.Vfs; + +private import tango.text.Util : head, locatePrior; + +/******************************************************************************* + + Virtual folders play host to other folder types, including both + concrete folder instances and subordinate virtual folders. You + can build a (singly rooted) tree from a set of virtual and non- + virtual folders, and treat them as though they were a combined + or single entity. For example, listing the contents of such a + tree is no different than listing the contents of a non-virtual + tree - there's just potentially more nodes to traverse. + +*******************************************************************************/ + +class VirtualFolder : VfsHost +{ + private char[] name_; + private VfsFile[char[]] files; + private VfsFolder[char[]] mounts; + private VfsFolderEntry[char[]] folders; + private VirtualFolder parent; + + /*********************************************************************** + + All folder must have a name. No '.' or '/' chars are + permitted + + ***********************************************************************/ + + this (char[] name) + { + validate (this.name_ = name); + } + + /*********************************************************************** + + Return the (short) name of this folder + + ***********************************************************************/ + + final char[] name() + { + return name_; + } + + /*********************************************************************** + + Return the (long) name of this folder. Virtual folders + do not have long names, since they don't relate directly + to a concrete folder instance + + ***********************************************************************/ + + final char[] toString() + { + return name; + } + + /*********************************************************************** + + Add a child folder. The child cannot 'overlap' with others + in the tree of the same type. Circular references across a + tree of virtual folders are detected and trapped. + + The second argument represents an optional name that the + mount should be known as, instead of the name exposed by + the provided folder (it is not an alias). + + ***********************************************************************/ + + VfsHost mount (VfsFolder folder, char[] name = null) + { + assert (folder); + if (name.length is 0) + name = folder.name; + + // link virtual children to us + auto child = cast(VirtualFolder) folder; + if (child) + if (child.parent) + error ("folder '"~name~"' belongs to another host"); + else + child.parent = this; + + // reach up to the root, and initiate tree sweep + auto root = this; + while (root.parent) + if (root is this) + error ("circular reference detected at '"~this.name~"' while mounting '"~name~"'"); + else + root = root.parent; + root.verify (folder, true); + + // all clear, so add the new folder + mounts [name] = folder; + return this; + } + + /*********************************************************************** + + Add a set of child folders. The children cannot 'overlap' + with others in the tree of the same type. Circular references + are detected and trapped. + + ***********************************************************************/ + + VfsHost mount (VfsFolders group) + { + foreach (folder; group) + mount (folder); + return this; + } + + /*********************************************************************** + + Unhook a child folder + + ***********************************************************************/ + + VfsHost dismount (VfsFolder folder) + { + char[] name = null; + + // check this is a child, and locate the mapped name + foreach (key, value; mounts) + if (folder is value) + name = key; + assert (name.ptr); + + // reach up to the root, and initiate tree sweep + auto root = this; + while (root.parent) + root = root.parent; + root.verify (folder, false); + + // all clear, so remove it + mounts.remove (name); + return this; + } + + /*********************************************************************** + + Add a symbolic link to another file. These are referenced + by file() alone, and do not show up in tree traversals + + ***********************************************************************/ + + final VfsHost map (VfsFile file, char[] name) + { + assert (name); + files[name] = file; + return this; + } + + /*********************************************************************** + + Add a symbolic link to another folder. These are referenced + by folder() alone, and do not show up in tree traversals + + ***********************************************************************/ + + final VfsHost map (VfsFolderEntry folder, char[] name) + { + assert (name); + folders[name] = folder; + return this; + } + + /*********************************************************************** + + 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; mounts) + { + VfsFolder x = folder; + if ((result = dg(x)) != 0) + break; + } + return result; + } + + /*********************************************************************** + + Return a folder representation of the given path. If the + path-head does not refer to an immediate child, and does + not match a symbolic link, it is considered unknown. + + ***********************************************************************/ + + final VfsFolderEntry folder (char[] path) + { + char[] tail; + auto text = head (path, FileConst.PathSeparatorString, tail); + + auto child = text in mounts; + if (child) + return child.folder (tail); + + auto sym = text in folders; + if (sym is null) + error ("'"~text~"' is not a recognized member of '"~name~"'"); + return *sym; + } + + /*********************************************************************** + + Return a file representation of the given path. If the + path-head does not refer to an immediate child folder, + and does not match a symbolic link, it is considered unknown. + + ***********************************************************************/ + + VfsFile file (char[] path) + { + auto tail = locatePrior (path, FileConst.PathSeparatorChar); + if (tail < path.length) + return folder(path[0..tail]).open.file(path[tail..$]); + + auto sym = path in files; + if (sym is null) + error ("'"~path~"' is not a recognized member of '"~name~"'"); + return *sym; + } + + /*********************************************************************** + + Clear the entire subtree. Use with caution + + ***********************************************************************/ + + final VfsFolder clear () + { + foreach (name, child; mounts) + child.clear; + return this; + } + + /*********************************************************************** + + Returns true if all of the children are writable + + ***********************************************************************/ + + final bool writable () + { + foreach (name, child; mounts) + if (! child.writable) + return false; + return true; + } + + /*********************************************************************** + + Returns a folder set containing only this one. Statistics + are inclusive of entries within this folder only, which + should be zero since symbolic links are not included + + ***********************************************************************/ + + final VfsFolders self () + { + return new VirtualFolders (this, false); + } + + /*********************************************************************** + + Returns a subtree of folders. Statistics are inclusive of + all files and folders throughout the sub-tree + + ***********************************************************************/ + + final VfsFolders tree () + { + return new VirtualFolders (this, true); + } + + /*********************************************************************** + + Sweep the subtree of mountpoints, testing a new folder + against all others. This propogates a folder test down + throughout the tree, where each folder implementation + should take appropriate action + + ***********************************************************************/ + + final void verify (VfsFolder folder, bool mounting) + { + foreach (name, child; mounts) + child.verify (folder, mounting); + } + + /*********************************************************************** + + 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) + { + foreach (name, child; mounts) + child.close (commit); + return this; + } + + /*********************************************************************** + + Throw an exception + + ***********************************************************************/ + + package final char[] error (char[] msg) + { + throw new VfsException (msg); + } + + /*********************************************************************** + + Validate path names + + ***********************************************************************/ + + private final void validate (char[] name) + { + assert (name); + if (locatePrior(name, '.') != name.length || + locatePrior(name, FileConst.PathSeparatorChar) != name.length) + error ("'"~name~"' contains invalid characters"); + } +} + + +/******************************************************************************* + + A set of virtual folders. For a sub-tree, we compose the results + of all our subordinates and delegate subsequent request to that + group. + +*******************************************************************************/ + +private class VirtualFolders : VfsFolders +{ + private VfsFolders[] 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 (VirtualFolder root, bool recurse) + { + if (recurse) + foreach (name, folder; root.mounts) + members ~= folder.tree; + } + + /*********************************************************************** + + Iterate over the set of contained VfsFolder instances + + ***********************************************************************/ + + final int opApply (int delegate(inout VfsFolder) dg) + { + int ret; + + foreach (group; members) + foreach (folder; group) + { + auto x = cast(VfsFolder) folder; + if ((ret = dg(x)) != 0) + break; + } + return ret; + } + + /*********************************************************************** + + Return the number of files in this group + + ***********************************************************************/ + + final uint files () + { + uint files; + foreach (group; members) + files += group.files; + return files; + } + + /*********************************************************************** + + Return the total size of all files in this group + + ***********************************************************************/ + + final ulong bytes () + { + ulong bytes; + foreach (group; members) + bytes += group.bytes; + return bytes; + } + + /*********************************************************************** + + Return the number of folders in this group + + ***********************************************************************/ + + final uint folders () + { + uint count; + foreach (group; members) + count += group.folders; + return count; + } + + /*********************************************************************** + + Return the total number of entries in this group + + ***********************************************************************/ + + final uint entries () + { + uint count; + foreach (group; members) + count += group.entries; + return count; + } + + /*********************************************************************** + + Return a subset of folders matching the given pattern + + ***********************************************************************/ + + final VfsFolders subset (char[] pattern) + { + auto set = new VirtualFolders; + + foreach (group; members) + set.members ~= group.subset (pattern); + return set; + } + + /*********************************************************************** + + Return a set of files matching the given pattern + + ***********************************************************************/ + + final VfsFiles catalog (char[] pattern) + { + return catalog ((VfsInfo info){return patternMatch (info.name, pattern);}); + } + + /*********************************************************************** + + Returns a set of files conforming to the given filter + + ***********************************************************************/ + + final VfsFiles catalog (VfsFilter filter = null) + { + return new VirtualFiles (this, filter); + } +} + + +/******************************************************************************* + + A set of virtual files, represented by composing the results of + the given set of folders. Subsequent calls are delegated to the + results from those folders + +*******************************************************************************/ + +private class VirtualFiles : VfsFiles +{ + private VfsFiles[] members; + + /*********************************************************************** + + ***********************************************************************/ + + private this (VirtualFolders host, VfsFilter filter) + { + foreach (group; host.members) + members ~= group.catalog (filter); + } + + /*********************************************************************** + + Iterate over the set of contained VfsFile instances + + ***********************************************************************/ + + final int opApply (int delegate(inout VfsFile) dg) + { + int ret; + + foreach (group; members) + foreach (file; group) + if ((ret = dg(file)) != 0) + break; + return ret; + } + + /*********************************************************************** + + Return the total number of entries + + ***********************************************************************/ + + final uint files () + { + uint count; + foreach (group; members) + count += group.files; + return count; + } + + /*********************************************************************** + + Return the total size of all files + + ***********************************************************************/ + + final ulong bytes () + { + ulong count; + foreach (group; members) + count += group.bytes; + return count; + } +} + + +debug (VirtualFolder) +{ +/******************************************************************************* + +*******************************************************************************/ + +import tango.io.Stdout; +import tango.io.Buffer; +import tango.io.vfs.FileFolder; + +void main() +{ + auto root = new VirtualFolder ("root"); + auto sub = new VirtualFolder ("sub"); + sub.mount (new FileFolder (r"d:/d/import/tango")); + + root.mount (sub) + .mount (new FileFolder (r"c:/"), "windows") + .mount (new FileFolder (r"d:/d/import/temp")); + + auto folder = root.folder (r"temp/bar"); + Stdout.formatln ("folder = {}", folder); + + root.map (root.folder(r"temp/subtree"), "fsym") + .map (root.file(r"temp/subtree/test.txt"), "wumpus"); + auto file = root.file (r"wumpus"); + Stdout.formatln ("file = {}", file); + Stdout.formatln ("fsym = {}", root.folder(r"fsym").open.file("test.txt")); + + foreach (folder; root.folder(r"temp/subtree").open) + Stdout.formatln ("folder.child '{}'", folder.name); + + auto set = root.self; + Stdout.formatln ("self.files = {}", set.files); + Stdout.formatln ("self.bytes = {}", set.bytes); + Stdout.formatln ("self.folders = {}", set.folders); + + set = root.folder("temp").open.tree; + Stdout.formatln ("tree.files = {}", set.files); + Stdout.formatln ("tree.bytes = {}", set.bytes); + Stdout.formatln ("tree.folders = {}", set.folders); + + foreach (folder; set) + Stdout.formatln ("tree.folder '{}' has {} files", folder.name, folder.self.files); + + auto cat = set.catalog ("*.txt"); + Stdout.formatln ("cat.files = {}", cat.files); + Stdout.formatln ("cat.bytes = {}", cat.bytes); + foreach (file; cat) + Stdout.formatln ("cat.name '{}' '{}'", file.name, file.toString); +} +}