Mercurial > projects > ldc
view 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 source
/******************************************************************************* 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); } }