|
1 | 1 | part of file.src.backends.memory; |
2 | 2 |
|
3 | | -class _MemoryDirectory extends _MemoryFileSystemEntity with Directory { |
| 3 | +class _MemoryDirectory extends _MemoryFileSystemEntity implements Directory { |
| 4 | + static int _tempCounter = 0; |
| 5 | + |
4 | 6 | _MemoryDirectory(MemoryFileSystem fileSystem, String path) |
5 | 7 | : super(fileSystem, path); |
6 | | - // Create an object representing a directory with no files. |
| 8 | + |
| 9 | + @override |
| 10 | + io.FileSystemEntityType get expectedType => io.FileSystemEntityType.DIRECTORY; |
| 11 | + |
| 12 | + @override |
| 13 | + Uri get uri => new Uri.directory(path); |
| 14 | + |
| 15 | + @override |
| 16 | + bool existsSync() => backingOrNull?.stat?.type == expectedType; |
| 17 | + |
7 | 18 | @override |
8 | | - Object _createImpl() => {}; |
| 19 | + Future<Directory> create({bool recursive: false}) async { |
| 20 | + createSync(recursive: recursive); |
| 21 | + return this; |
| 22 | + } |
9 | 23 |
|
10 | 24 | @override |
11 | | - Stream<FileSystemEntity> list({bool recursive: false}) async* { |
12 | | - var directory = _resolve(false); |
13 | | - if (directory == null) { |
14 | | - throw new FileSystemEntityException('Not found', path); |
| 25 | + void createSync({bool recursive: false}) { |
| 26 | + _Node node = _createSync( |
| 27 | + (_DirectoryNode parent, bool isFinalSegment) { |
| 28 | + if (recursive || isFinalSegment) { |
| 29 | + return new _DirectoryNode(parent); |
| 30 | + } |
| 31 | + return null; |
| 32 | + }, |
| 33 | + ); |
| 34 | + if (node.type != expectedType) { |
| 35 | + // There was an existing non-directory node at this object's path |
| 36 | + throw new io.FileSystemException('Creation failed', path); |
15 | 37 | } |
16 | | - if (name != '') { |
17 | | - directory = directory[name] as Map<String, Object>; |
| 38 | + } |
| 39 | + |
| 40 | + @override |
| 41 | + Future<Directory> createTemp([String prefix]) async => createTempSync(prefix); |
| 42 | + |
| 43 | + @override |
| 44 | + Directory createTempSync([String prefix]) { |
| 45 | + prefix ??= ''; |
| 46 | + String fullPath = '$path$_separator$prefix'; |
| 47 | + String dirname = fileSystem._context.dirname(fullPath); |
| 48 | + String basename = fileSystem._context.basename(fullPath); |
| 49 | + _DirectoryNode node = fileSystem._findNode(dirname); |
| 50 | + _checkExists(node, () => dirname); |
| 51 | + var name = () => '$basename$_tempCounter'; |
| 52 | + while (node.children.containsKey(name())) { |
| 53 | + _tempCounter++; |
18 | 54 | } |
19 | | - // This could be optimized heavily, right now it makes a lot of extra |
20 | | - // lookups and gets more and more expensive as you traverse downwards. |
21 | | - for (var name in directory.keys) { |
22 | | - var entityPath = '$path/$name'; |
23 | | - if (await fileSystem.type(entityPath) == FileSystemEntityType.FILE) { |
24 | | - yield fileSystem.file(entityPath); |
25 | | - } else { |
26 | | - yield fileSystem.directory(entityPath); |
27 | | - if (recursive) { |
28 | | - yield* fileSystem.directory(entityPath).list(recursive: true); |
| 55 | + _DirectoryNode tempDir = new _DirectoryNode(node); |
| 56 | + node.children[name()] = tempDir; |
| 57 | + return new _MemoryDirectory(fileSystem, '$dirname$_separator${name()}'); |
| 58 | + } |
| 59 | + |
| 60 | + @override |
| 61 | + Future<Directory> rename(String newPath) async => renameSync(newPath); |
| 62 | + |
| 63 | + @override |
| 64 | + Directory renameSync(String newPath) => _renameSync( |
| 65 | + newPath, |
| 66 | + validateOverwriteExistingEntity: (_DirectoryNode existingNode) { |
| 67 | + if (existingNode.children.isNotEmpty) { |
| 68 | + throw new io.FileSystemException('Directory not empty', newPath); |
| 69 | + } |
| 70 | + }, |
| 71 | + ); |
| 72 | + |
| 73 | + @override |
| 74 | + Directory get parent => |
| 75 | + (backingOrNull?.isRoot ?? false) ? this : super.parent; |
| 76 | + |
| 77 | + @override |
| 78 | + Directory get absolute => super.absolute; |
| 79 | + |
| 80 | + @override |
| 81 | + Stream<FileSystemEntity> list({ |
| 82 | + bool recursive: false, |
| 83 | + bool followLinks: true, |
| 84 | + }) => |
| 85 | + new Stream.fromIterable(listSync( |
| 86 | + recursive: recursive, |
| 87 | + followLinks: followLinks, |
| 88 | + )); |
| 89 | + |
| 90 | + @override |
| 91 | + List<FileSystemEntity> listSync({ |
| 92 | + bool recursive: false, |
| 93 | + bool followLinks: true, |
| 94 | + }) { |
| 95 | + _DirectoryNode node = backing; |
| 96 | + List<FileSystemEntity> listing = <FileSystemEntity>[]; |
| 97 | + Set<_LinkNode> visitedLinks = new Set<_LinkNode>(); |
| 98 | + List<_PendingListTask> tasks = <_PendingListTask>[ |
| 99 | + new _PendingListTask( |
| 100 | + node, |
| 101 | + path.endsWith(_separator) ? path.substring(0, path.length - 1) : path, |
| 102 | + ), |
| 103 | + ]; |
| 104 | + while (tasks.isNotEmpty) { |
| 105 | + _PendingListTask task = tasks.removeLast(); |
| 106 | + task.dir.children.forEach((String name, _Node child) { |
| 107 | + String childPath = '${task.path}$_separator$name'; |
| 108 | + if (followLinks && _isLink(child) && visitedLinks.add(child)) { |
| 109 | + child = (child as _LinkNode).referent; |
29 | 110 | } |
30 | | - } |
| 111 | + if (_isDirectory(child)) { |
| 112 | + listing.add(new _MemoryDirectory(fileSystem, childPath)); |
| 113 | + if (recursive) { |
| 114 | + tasks.add(new _PendingListTask(child, childPath)); |
| 115 | + } |
| 116 | + } else if (_isLink(child)) { |
| 117 | + listing.add(new _MemoryLink(fileSystem, childPath)); |
| 118 | + } else if (_isFile(child)) { |
| 119 | + listing.add(new _MemoryFile(fileSystem, childPath)); |
| 120 | + } |
| 121 | + }); |
31 | 122 | } |
| 123 | + return listing; |
32 | 124 | } |
33 | 125 |
|
34 | 126 | @override |
35 | | - final FileSystemEntityType _type = FileSystemEntityType.DIRECTORY; |
| 127 | + Directory _clone(String path) => new _MemoryDirectory(fileSystem, path); |
| 128 | +} |
| 129 | + |
| 130 | +class _PendingListTask { |
| 131 | + final _DirectoryNode dir; |
| 132 | + final String path; |
| 133 | + _PendingListTask(this.dir, this.path); |
36 | 134 | } |
0 commit comments