| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of file.src.backends.memory; |
| |
| /// A class that represents the actual storage of an existent file system |
| /// entity (whereas classes [File], [Directory], and [Link] represent less |
| /// concrete entities that may or may not yet exist). |
| /// |
| /// This data structure is loosely based on a Unix-style file system inode |
| /// (hence the name). |
| abstract class _Node { |
| _DirectoryNode _parent; |
| |
| _Node(this._parent) { |
| if (_parent == null && !isRoot) { |
| throw new io.FileSystemException('All nodes must have a parent.'); |
| } |
| } |
| |
| /// Gets the directory that holds this node. |
| _DirectoryNode get parent => _parent; |
| |
| /// Reparents this node to live in the specified directory. |
| set parent(_DirectoryNode parent) { |
| assert(parent != null); |
| _DirectoryNode ancestor = parent; |
| while (!ancestor.isRoot) { |
| if (ancestor == this) { |
| throw new io.FileSystemException( |
| 'A directory cannot be its own ancestor.'); |
| } |
| ancestor = ancestor.parent; |
| } |
| _parent = parent; |
| } |
| |
| /// Returns the type of the file system entity that this node represents. |
| io.FileSystemEntityType get type; |
| |
| /// Returns the POSIX stat information for this file system object. |
| io.FileStat get stat; |
| |
| /// Returns the closest directory in the ancestry hierarchy starting with |
| /// this node. For directory nodes, it returns the node itself; for other |
| /// nodes, it returns the parent node. |
| _DirectoryNode get directory => _parent; |
| |
| /// Tells if this node is a root node. |
| bool get isRoot => false; |
| |
| // Returns the file system responsible for this node. |
| MemoryFileSystem get fs => _parent.fs; |
| } |
| |
| /// Base class that represents the backing for those nodes that have |
| /// substance (namely, node types that will not redirect to other types when |
| /// you call [stat] on them). |
| abstract class _RealNode extends _Node { |
| int changed; |
| int modified; |
| int accessed; |
| int mode = 0x777; |
| |
| _RealNode(_DirectoryNode parent) : super(parent) { |
| int now = new DateTime.now().millisecondsSinceEpoch; |
| changed = now; |
| modified = now; |
| accessed = now; |
| } |
| |
| @override |
| io.FileStat get stat { |
| return new _MemoryFileStat( |
| new DateTime.fromMillisecondsSinceEpoch(changed), |
| new DateTime.fromMillisecondsSinceEpoch(modified), |
| new DateTime.fromMillisecondsSinceEpoch(accessed), |
| type, |
| mode, |
| size, |
| ); |
| } |
| |
| /// The size of the file system entity in bytes. |
| int get size; |
| |
| /// Updates the last modified time of the node. |
| void touch() { |
| modified = new DateTime.now().millisecondsSinceEpoch; |
| } |
| } |
| |
| /// Class that represents the backing for an in-memory directory. |
| class _DirectoryNode extends _RealNode { |
| final Map<String, _Node> children = <String, _Node>{}; |
| |
| _DirectoryNode(_DirectoryNode parent) : super(parent); |
| |
| @override |
| io.FileSystemEntityType get type => io.FileSystemEntityType.DIRECTORY; |
| |
| @override |
| _DirectoryNode get directory => this; |
| |
| @override |
| int get size => 0; |
| } |
| |
| /// Class that represents the backing for the root of the in-memory file system. |
| class _RootNode extends _DirectoryNode { |
| final MemoryFileSystem _fs; |
| |
| _RootNode(this._fs) : super(null) { |
| assert(_fs != null); |
| assert(_fs._root == null); |
| } |
| |
| @override |
| _DirectoryNode get parent => this; |
| |
| @override |
| bool get isRoot => true; |
| |
| @override |
| set parent(_DirectoryNode parent) => throw new UnsupportedError( |
| 'Cannot set the parent of the root directory.'); |
| |
| @override |
| MemoryFileSystem get fs => _fs; |
| } |
| |
| /// Class that represents the backing for an in-memory regular file. |
| class _FileNode extends _RealNode { |
| List<int> content = <int>[]; |
| |
| _FileNode(_DirectoryNode parent) : super(parent); |
| |
| @override |
| io.FileSystemEntityType get type => io.FileSystemEntityType.FILE; |
| |
| @override |
| int get size => content.length; |
| |
| void copyFrom(_FileNode source) { |
| modified = changed = new DateTime.now().millisecondsSinceEpoch; |
| accessed = source.accessed; |
| mode = source.mode; |
| content = new List<int>.from(source.content); |
| } |
| } |
| |
| /// Class that represents the backing for an in-memory symbolic link. |
| class _LinkNode extends _Node { |
| String target; |
| bool reentrant = false; |
| |
| _LinkNode(_DirectoryNode parent, this.target) : super(parent) { |
| assert(target != null && target.isNotEmpty); |
| } |
| |
| /// Gets the node backing for this link's target. Throws a |
| /// [FileSystemException] if this link references a non-existent file |
| /// system entity. |
| /// |
| /// If [tailVisitor] is specified, it will be invoked for the tail path |
| /// segment of this link's target, and its return value will be used as the |
| /// return value of this method. If the tail path segment of this link's |
| /// target cannot be traversed into, a [FileSystemException] will be thrown, |
| /// and [tailVisitor] will not be invoked. |
| _Node getReferent({ |
| _Node tailVisitor(_DirectoryNode parent, String childName, _Node child), |
| }) { |
| _Node referent = fs._findNode( |
| target, |
| reference: this, |
| segmentVisitor: ( |
| _DirectoryNode parent, |
| String childName, |
| _Node child, |
| int currentSegment, |
| int finalSegment, |
| ) { |
| if (tailVisitor != null && currentSegment == finalSegment) { |
| child = tailVisitor(parent, childName, child); |
| } |
| return child; |
| }, |
| ); |
| _checkExists(referent, () => target); |
| return referent; |
| } |
| |
| /// Gets the node backing for this link's target, or null if this link |
| /// references a non-existent file system entity. |
| _Node get referentOrNull { |
| try { |
| return getReferent(); |
| } on io.FileSystemException { |
| return null; |
| } |
| } |
| |
| @override |
| io.FileSystemEntityType get type => io.FileSystemEntityType.LINK; |
| |
| @override |
| io.FileStat get stat { |
| if (reentrant) { |
| return _MemoryFileStat._notFound; |
| } |
| reentrant = true; |
| try { |
| _Node node = referentOrNull; |
| return node == null ? _MemoryFileStat._notFound : node.stat; |
| } finally { |
| reentrant = false; |
| } |
| } |
| } |