| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library memory_file_system; | 5 library analyzer.file_system.memory_file_system; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:core' hide Resource; | 9 import 'dart:convert'; |
| 10 import 'dart:core'; |
| 10 | 11 |
| 11 import 'package:analyzer/src/generated/engine.dart' show TimestampedData; | 12 import 'package:analyzer/file_system/file_system.dart'; |
| 12 import 'package:analyzer/src/generated/source_io.dart'; | 13 import 'package:analyzer/src/generated/source_io.dart'; |
| 13 import 'package:path/path.dart'; | 14 import 'package:analyzer/src/source/source_resource.dart'; |
| 15 import 'package:analyzer/src/util/absolute_path.dart'; |
| 16 import 'package:path/path.dart' as pathos; |
| 14 import 'package:watcher/watcher.dart'; | 17 import 'package:watcher/watcher.dart'; |
| 15 | 18 |
| 16 import 'file_system.dart'; | |
| 17 | |
| 18 /** | 19 /** |
| 19 * An in-memory implementation of [ResourceProvider]. | 20 * An in-memory implementation of [ResourceProvider]. |
| 20 * Use `/` as a path separator. | 21 * Use `/` as a path separator. |
| 21 */ | 22 */ |
| 22 class MemoryResourceProvider implements ResourceProvider { | 23 class MemoryResourceProvider implements ResourceProvider { |
| 23 final Map<String, _MemoryResource> _pathToResource = | 24 final Map<String, _MemoryResource> _pathToResource = |
| 24 new HashMap<String, _MemoryResource>(); | 25 new HashMap<String, _MemoryResource>(); |
| 25 final Map<String, String> _pathToContent = new HashMap<String, String>(); | 26 final Map<String, List<int>> _pathToBytes = new HashMap<String, List<int>>(); |
| 26 final Map<String, int> _pathToTimestamp = new HashMap<String, int>(); | 27 final Map<String, int> _pathToTimestamp = new HashMap<String, int>(); |
| 27 final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers = | 28 final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers = |
| 28 new HashMap<String, List<StreamController<WatchEvent>>>(); | 29 new HashMap<String, List<StreamController<WatchEvent>>>(); |
| 29 int nextStamp = 0; | 30 int nextStamp = 0; |
| 30 | 31 |
| 32 final pathos.Context _pathContext; |
| 33 |
| 31 @override | 34 @override |
| 32 Context get pathContext => posix; | 35 final AbsolutePathContext absolutePathContext; |
| 36 |
| 37 MemoryResourceProvider( |
| 38 {pathos.Context context, @deprecated bool isWindows: false}) |
| 39 : _pathContext = (context ??= pathos.context), |
| 40 absolutePathContext = |
| 41 new AbsolutePathContext(context.style == pathos.Style.windows); |
| 42 |
| 43 @override |
| 44 pathos.Context get pathContext => _pathContext; |
| 45 |
| 46 /** |
| 47 * Convert the given posix [path] to conform to this provider's path context. |
| 48 * |
| 49 * This is a utility method for testing; paths passed in to other methods in |
| 50 * this class are never converted automatically. |
| 51 */ |
| 52 String convertPath(String path) { |
| 53 if (pathContext.style == pathos.windows.style && |
| 54 path.startsWith(pathos.posix.separator)) { |
| 55 path = r'C:' + |
| 56 path.replaceAll(pathos.posix.separator, pathos.windows.separator); |
| 57 } |
| 58 return path; |
| 59 } |
| 33 | 60 |
| 34 /** | 61 /** |
| 35 * Delete the file with the given path. | 62 * Delete the file with the given path. |
| 36 */ | 63 */ |
| 37 void deleteFile(String path) { | 64 void deleteFile(String path) { |
| 38 _checkFileAtPath(path); | 65 _checkFileAtPath(path); |
| 39 _pathToResource.remove(path); | 66 _pathToResource.remove(path); |
| 40 _pathToContent.remove(path); | 67 _pathToBytes.remove(path); |
| 41 _pathToTimestamp.remove(path); | 68 _pathToTimestamp.remove(path); |
| 42 _notifyWatchers(path, ChangeType.REMOVE); | 69 _notifyWatchers(path, ChangeType.REMOVE); |
| 43 } | 70 } |
| 44 | 71 |
| 45 /** | 72 /** |
| 46 * Delete the folder with the given path | 73 * Delete the folder with the given path |
| 47 * and recurively delete nested files and folders. | 74 * and recursively delete nested files and folders. |
| 48 */ | 75 */ |
| 49 void deleteFolder(String path) { | 76 void deleteFolder(String path) { |
| 50 _checkFolderAtPath(path); | 77 _checkFolderAtPath(path); |
| 51 _MemoryFolder folder = _pathToResource[path]; | 78 _MemoryFolder folder = _pathToResource[path]; |
| 52 for (Resource child in folder.getChildren()) { | 79 for (Resource child in folder.getChildren()) { |
| 53 if (child is File) { | 80 if (child is File) { |
| 54 deleteFile(child.path); | 81 deleteFile(child.path); |
| 55 } else if (child is Folder) { | 82 } else if (child is Folder) { |
| 56 deleteFolder(child.path); | 83 deleteFolder(child.path); |
| 57 } else { | 84 } else { |
| 58 throw 'failed to delete resource: $child'; | 85 throw 'failed to delete resource: $child'; |
| 59 } | 86 } |
| 60 } | 87 } |
| 61 _pathToResource.remove(path); | 88 _pathToResource.remove(path); |
| 62 _pathToContent.remove(path); | 89 _pathToBytes.remove(path); |
| 63 _pathToTimestamp.remove(path); | 90 _pathToTimestamp.remove(path); |
| 64 _notifyWatchers(path, ChangeType.REMOVE); | 91 _notifyWatchers(path, ChangeType.REMOVE); |
| 65 } | 92 } |
| 66 | 93 |
| 67 @override | 94 @override |
| 68 File getFile(String path) => new _MemoryFile(this, path); | 95 File getFile(String path) => new _MemoryFile(this, path); |
| 69 | 96 |
| 70 @override | 97 @override |
| 71 Folder getFolder(String path) => newFolder(path); | 98 Folder getFolder(String path) => newFolder(path); |
| 72 | 99 |
| 73 @override | 100 @override |
| 101 Future<List<int>> getModificationTimes(List<Source> sources) async { |
| 102 return sources.map((source) { |
| 103 String path = source.fullName; |
| 104 return _pathToTimestamp[path] ?? -1; |
| 105 }).toList(); |
| 106 } |
| 107 |
| 108 @override |
| 74 Resource getResource(String path) { | 109 Resource getResource(String path) { |
| 75 path = pathContext.normalize(path); | 110 path = pathContext.normalize(path); |
| 76 Resource resource = _pathToResource[path]; | 111 Resource resource = _pathToResource[path]; |
| 77 if (resource == null) { | 112 if (resource == null) { |
| 78 resource = new _MemoryFile(this, path); | 113 resource = new _MemoryFile(this, path); |
| 79 } | 114 } |
| 80 return resource; | 115 return resource; |
| 81 } | 116 } |
| 82 | 117 |
| 83 @override | 118 @override |
| 84 Folder getStateLocation(String pluginId) { | 119 Folder getStateLocation(String pluginId) { |
| 85 return newFolder('/user/home/$pluginId'); | 120 return newFolder('/user/home/$pluginId'); |
| 86 } | 121 } |
| 87 | 122 |
| 88 void modifyFile(String path, String content) { | 123 void modifyFile(String path, String content) { |
| 89 _checkFileAtPath(path); | 124 _checkFileAtPath(path); |
| 90 _pathToContent[path] = content; | 125 _pathToBytes[path] = UTF8.encode(content); |
| 91 _pathToTimestamp[path] = nextStamp++; | 126 _pathToTimestamp[path] = nextStamp++; |
| 92 _notifyWatchers(path, ChangeType.MODIFY); | 127 _notifyWatchers(path, ChangeType.MODIFY); |
| 93 } | 128 } |
| 94 | 129 |
| 95 /** | 130 /** |
| 96 * Create a resource representing a dummy link (that is, a File object which | 131 * Create a resource representing a dummy link (that is, a File object which |
| 97 * appears in its parent directory, but whose `exists` property is false) | 132 * appears in its parent directory, but whose `exists` property is false) |
| 98 */ | 133 */ |
| 99 File newDummyLink(String path) { | 134 File newDummyLink(String path) { |
| 100 path = pathContext.normalize(path); | 135 path = pathContext.normalize(path); |
| 101 newFolder(pathContext.dirname(path)); | 136 newFolder(pathContext.dirname(path)); |
| 102 _MemoryDummyLink link = new _MemoryDummyLink(this, path); | 137 _MemoryDummyLink link = new _MemoryDummyLink(this, path); |
| 103 _pathToResource[path] = link; | 138 _pathToResource[path] = link; |
| 104 _pathToTimestamp[path] = nextStamp++; | 139 _pathToTimestamp[path] = nextStamp++; |
| 105 _notifyWatchers(path, ChangeType.ADD); | 140 _notifyWatchers(path, ChangeType.ADD); |
| 106 return link; | 141 return link; |
| 107 } | 142 } |
| 108 | 143 |
| 109 File newFile(String path, String content, [int stamp]) { | 144 File newFile(String path, String content, [int stamp]) { |
| 110 path = pathContext.normalize(path); | 145 path = pathContext.normalize(path); |
| 111 _MemoryResource folder = _pathToResource[pathContext.dirname(path)]; | 146 _MemoryFile file = _newFile(path); |
| 112 if (folder == null) { | 147 _pathToBytes[path] = UTF8.encode(content); |
| 113 newFolder(pathContext.dirname(path)); | 148 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| 114 } else if (folder is! Folder) { | 149 _notifyWatchers(path, ChangeType.ADD); |
| 115 throw new ArgumentError('Cannot create file ($path) as child of file'); | 150 return file; |
| 116 } | 151 } |
| 117 _MemoryFile file = new _MemoryFile(this, path); | 152 |
| 118 _pathToResource[path] = file; | 153 File newFileWithBytes(String path, List<int> bytes, [int stamp]) { |
| 119 _pathToContent[path] = content; | 154 path = pathContext.normalize(path); |
| 120 _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++; | 155 _MemoryFile file = _newFile(path); |
| 156 _pathToBytes[path] = bytes; |
| 157 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| 121 _notifyWatchers(path, ChangeType.ADD); | 158 _notifyWatchers(path, ChangeType.ADD); |
| 122 return file; | 159 return file; |
| 123 } | 160 } |
| 124 | 161 |
| 125 Folder newFolder(String path) { | 162 Folder newFolder(String path) { |
| 126 path = pathContext.normalize(path); | 163 path = pathContext.normalize(path); |
| 127 if (!path.startsWith(pathContext.separator)) { | 164 if (!pathContext.isAbsolute(path)) { |
| 128 throw new ArgumentError( | 165 throw new ArgumentError("Path must be absolute : $path"); |
| 129 "Path must start with '${pathContext.separator}' : $path"); | |
| 130 } | 166 } |
| 131 _MemoryResource resource = _pathToResource[path]; | 167 _MemoryResource resource = _pathToResource[path]; |
| 132 if (resource == null) { | 168 if (resource == null) { |
| 133 String parentPath = pathContext.dirname(path); | 169 String parentPath = pathContext.dirname(path); |
| 134 if (parentPath != path) { | 170 if (parentPath != path) { |
| 135 newFolder(parentPath); | 171 newFolder(parentPath); |
| 136 } | 172 } |
| 137 _MemoryFolder folder = new _MemoryFolder(this, path); | 173 _MemoryFolder folder = new _MemoryFolder(this, path); |
| 138 _pathToResource[path] = folder; | 174 _pathToResource[path] = folder; |
| 139 _pathToTimestamp[path] = nextStamp++; | 175 _pathToTimestamp[path] = nextStamp++; |
| 140 _notifyWatchers(path, ChangeType.ADD); | 176 _notifyWatchers(path, ChangeType.ADD); |
| 141 return folder; | 177 return folder; |
| 142 } else if (resource is _MemoryFolder) { | 178 } else if (resource is _MemoryFolder) { |
| 143 _notifyWatchers(path, ChangeType.ADD); | 179 _notifyWatchers(path, ChangeType.ADD); |
| 144 return resource; | 180 return resource; |
| 145 } else { | 181 } else { |
| 146 String message = | 182 String message = |
| 147 'Folder expected at ' "'$path'" 'but ${resource.runtimeType} found'; | 183 'Folder expected at ' "'$path'" 'but ${resource.runtimeType} found'; |
| 148 throw new ArgumentError(message); | 184 throw new ArgumentError(message); |
| 149 } | 185 } |
| 150 } | 186 } |
| 151 | 187 |
| 188 _MemoryFile renameFileSync(_MemoryFile file, String newPath) { |
| 189 String path = file.path; |
| 190 if (newPath == path) { |
| 191 return file; |
| 192 } |
| 193 _MemoryResource existingNewResource = _pathToResource[newPath]; |
| 194 if (existingNewResource is _MemoryFolder) { |
| 195 throw new FileSystemException( |
| 196 path, 'Could not be renamed: $newPath is a folder.'); |
| 197 } |
| 198 _MemoryFile newFile = _newFile(newPath); |
| 199 _pathToResource.remove(path); |
| 200 _pathToBytes[newPath] = _pathToBytes.remove(path); |
| 201 _pathToTimestamp[newPath] = _pathToTimestamp.remove(path); |
| 202 if (existingNewResource != null) { |
| 203 _notifyWatchers(newPath, ChangeType.REMOVE); |
| 204 } |
| 205 _notifyWatchers(path, ChangeType.REMOVE); |
| 206 _notifyWatchers(newPath, ChangeType.ADD); |
| 207 return newFile; |
| 208 } |
| 209 |
| 152 File updateFile(String path, String content, [int stamp]) { | 210 File updateFile(String path, String content, [int stamp]) { |
| 153 path = pathContext.normalize(path); | 211 path = pathContext.normalize(path); |
| 154 newFolder(pathContext.dirname(path)); | 212 newFolder(pathContext.dirname(path)); |
| 155 _MemoryFile file = new _MemoryFile(this, path); | 213 _MemoryFile file = new _MemoryFile(this, path); |
| 156 _pathToResource[path] = file; | 214 _pathToResource[path] = file; |
| 157 _pathToContent[path] = content; | 215 _pathToBytes[path] = UTF8.encode(content); |
| 158 _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++; | 216 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| 159 _notifyWatchers(path, ChangeType.MODIFY); | 217 _notifyWatchers(path, ChangeType.MODIFY); |
| 160 return file; | 218 return file; |
| 161 } | 219 } |
| 162 | 220 |
| 221 /** |
| 222 * Write a representation of the file system on the given [sink]. |
| 223 */ |
| 224 void writeOn(StringSink sink) { |
| 225 List<String> paths = _pathToResource.keys.toList(); |
| 226 paths.sort(); |
| 227 paths.forEach(sink.writeln); |
| 228 } |
| 229 |
| 163 void _checkFileAtPath(String path) { | 230 void _checkFileAtPath(String path) { |
| 164 _MemoryResource resource = _pathToResource[path]; | 231 _MemoryResource resource = _pathToResource[path]; |
| 165 if (resource is! _MemoryFile) { | 232 if (resource is! _MemoryFile) { |
| 166 throw new ArgumentError( | 233 throw new ArgumentError( |
| 167 'File expected at "$path" but ${resource.runtimeType} found'); | 234 'File expected at "$path" but ${resource.runtimeType} found'); |
| 168 } | 235 } |
| 169 } | 236 } |
| 170 | 237 |
| 171 void _checkFolderAtPath(String path) { | 238 void _checkFolderAtPath(String path) { |
| 172 _MemoryResource resource = _pathToResource[path]; | 239 _MemoryResource resource = _pathToResource[path]; |
| 173 if (resource is! _MemoryFolder) { | 240 if (resource is! _MemoryFolder) { |
| 174 throw new ArgumentError( | 241 throw new ArgumentError( |
| 175 'Folder expected at "$path" but ${resource.runtimeType} found'); | 242 'Folder expected at "$path" but ${resource.runtimeType} found'); |
| 176 } | 243 } |
| 177 } | 244 } |
| 178 | 245 |
| 246 /** |
| 247 * Create a new [_MemoryFile] without any content. |
| 248 */ |
| 249 _MemoryFile _newFile(String path) { |
| 250 String folderPath = pathContext.dirname(path); |
| 251 _MemoryResource folder = _pathToResource[folderPath]; |
| 252 if (folder == null) { |
| 253 newFolder(folderPath); |
| 254 } else if (folder is! Folder) { |
| 255 throw new ArgumentError('Cannot create file ($path) as child of file'); |
| 256 } |
| 257 _MemoryFile file = new _MemoryFile(this, path); |
| 258 _pathToResource[path] = file; |
| 259 return file; |
| 260 } |
| 261 |
| 179 void _notifyWatchers(String path, ChangeType changeType) { | 262 void _notifyWatchers(String path, ChangeType changeType) { |
| 180 _pathToWatchers.forEach((String watcherPath, | 263 _pathToWatchers.forEach((String watcherPath, |
| 181 List<StreamController<WatchEvent>> streamControllers) { | 264 List<StreamController<WatchEvent>> streamControllers) { |
| 182 if (watcherPath == path || pathContext.isWithin(watcherPath, path)) { | 265 if (watcherPath == path || pathContext.isWithin(watcherPath, path)) { |
| 183 for (StreamController<WatchEvent> streamController | 266 for (StreamController<WatchEvent> streamController |
| 184 in streamControllers) { | 267 in streamControllers) { |
| 185 streamController.add(new WatchEvent(changeType, path)); | 268 streamController.add(new WatchEvent(changeType, path)); |
| 186 } | 269 } |
| 187 } | 270 } |
| 188 }); | 271 }); |
| 189 } | 272 } |
| 273 |
| 274 void _setFileContent(_MemoryFile file, List<int> bytes) { |
| 275 String path = file.path; |
| 276 _pathToResource[path] = file; |
| 277 _pathToBytes[path] = bytes; |
| 278 _pathToTimestamp[path] = nextStamp++; |
| 279 _notifyWatchers(path, ChangeType.MODIFY); |
| 280 } |
| 190 } | 281 } |
| 191 | 282 |
| 192 /** | 283 /** |
| 193 * An in-memory implementation of [File] which acts like a symbolic link to a | 284 * An in-memory implementation of [File] which acts like a symbolic link to a |
| 194 * non-existent file. | 285 * non-existent file. |
| 195 */ | 286 */ |
| 196 class _MemoryDummyLink extends _MemoryResource implements File { | 287 class _MemoryDummyLink extends _MemoryResource implements File { |
| 197 _MemoryDummyLink(MemoryResourceProvider provider, String path) | 288 _MemoryDummyLink(MemoryResourceProvider provider, String path) |
| 198 : super(provider, path); | 289 : super(provider, path); |
| 199 | 290 |
| 200 @override | 291 @override |
| 201 Stream<WatchEvent> get changes { | 292 Stream<WatchEvent> get changes { |
| 202 throw new FileSystemException(path, "File does not exist"); | 293 throw new FileSystemException(path, "File does not exist"); |
| 203 } | 294 } |
| 204 | 295 |
| 205 @override | 296 @override |
| 206 bool get exists => false; | 297 bool get exists => false; |
| 207 | 298 |
| 299 @override |
| 208 int get modificationStamp { | 300 int get modificationStamp { |
| 209 int stamp = _provider._pathToTimestamp[path]; | 301 int stamp = _provider._pathToTimestamp[path]; |
| 210 if (stamp == null) { | 302 if (stamp == null) { |
| 211 throw new FileSystemException(path, "File does not exist"); | 303 throw new FileSystemException(path, "File does not exist"); |
| 212 } | 304 } |
| 213 return stamp; | 305 return stamp; |
| 214 } | 306 } |
| 215 | 307 |
| 216 String get _content { | |
| 217 throw new FileSystemException(path, 'File could not be read'); | |
| 218 } | |
| 219 | |
| 220 @override | 308 @override |
| 221 Source createSource([Uri uri]) { | 309 Source createSource([Uri uri]) { |
| 222 throw new FileSystemException(path, 'File could not be read'); | 310 throw new FileSystemException(path, 'File could not be read'); |
| 223 } | 311 } |
| 224 | 312 |
| 225 @override | 313 @override |
| 314 void delete() { |
| 315 throw new FileSystemException(path, 'File could not be deleted'); |
| 316 } |
| 317 |
| 318 @override |
| 226 bool isOrContains(String path) { | 319 bool isOrContains(String path) { |
| 227 return path == this.path; | 320 return path == this.path; |
| 228 } | 321 } |
| 229 | 322 |
| 230 @override | 323 @override |
| 324 List<int> readAsBytesSync() { |
| 325 throw new FileSystemException(path, 'File could not be read'); |
| 326 } |
| 327 |
| 328 @override |
| 231 String readAsStringSync() { | 329 String readAsStringSync() { |
| 232 throw new FileSystemException(path, 'File could not be read'); | 330 throw new FileSystemException(path, 'File could not be read'); |
| 233 } | 331 } |
| 332 |
| 333 @override |
| 334 File renameSync(String newPath) { |
| 335 throw new FileSystemException(path, 'File could not be renamed'); |
| 336 } |
| 337 |
| 338 @override |
| 339 File resolveSymbolicLinksSync() { |
| 340 return throw new FileSystemException(path, "File does not exist"); |
| 341 } |
| 342 |
| 343 @override |
| 344 void writeAsBytesSync(List<int> bytes) { |
| 345 throw new FileSystemException(path, 'File could not be written'); |
| 346 } |
| 347 |
| 348 @override |
| 349 void writeAsStringSync(String content) { |
| 350 throw new FileSystemException(path, 'File could not be written'); |
| 351 } |
| 234 } | 352 } |
| 235 | 353 |
| 236 /** | 354 /** |
| 237 * An in-memory implementation of [File]. | 355 * An in-memory implementation of [File]. |
| 238 */ | 356 */ |
| 239 class _MemoryFile extends _MemoryResource implements File { | 357 class _MemoryFile extends _MemoryResource implements File { |
| 240 _MemoryFile(MemoryResourceProvider provider, String path) | 358 _MemoryFile(MemoryResourceProvider provider, String path) |
| 241 : super(provider, path); | 359 : super(provider, path); |
| 242 | 360 |
| 243 @override | 361 @override |
| 244 bool get exists => _provider._pathToResource[path] is _MemoryFile; | 362 bool get exists => _provider._pathToResource[path] is _MemoryFile; |
| 245 | 363 |
| 364 @override |
| 246 int get modificationStamp { | 365 int get modificationStamp { |
| 247 int stamp = _provider._pathToTimestamp[path]; | 366 int stamp = _provider._pathToTimestamp[path]; |
| 248 if (stamp == null) { | 367 if (stamp == null) { |
| 249 throw new FileSystemException(path, 'File "$path" does not exist.'); | 368 throw new FileSystemException(path, 'File "$path" does not exist.'); |
| 250 } | 369 } |
| 251 return stamp; | 370 return stamp; |
| 252 } | 371 } |
| 253 | 372 |
| 254 String get _content { | |
| 255 String content = _provider._pathToContent[path]; | |
| 256 if (content == null) { | |
| 257 throw new FileSystemException(path, 'File "$path" does not exist.'); | |
| 258 } | |
| 259 return content; | |
| 260 } | |
| 261 | |
| 262 @override | 373 @override |
| 263 Source createSource([Uri uri]) { | 374 Source createSource([Uri uri]) { |
| 264 if (uri == null) { | 375 uri ??= _provider.pathContext.toUri(path); |
| 265 uri = _provider.pathContext.toUri(path); | 376 return new FileSource(this, uri); |
| 266 } | |
| 267 return new _MemoryFileSource(this, uri); | |
| 268 } | 377 } |
| 269 | 378 |
| 270 @override | 379 @override |
| 380 void delete() { |
| 381 _provider.deleteFile(path); |
| 382 } |
| 383 |
| 384 @override |
| 271 bool isOrContains(String path) { | 385 bool isOrContains(String path) { |
| 272 return path == this.path; | 386 return path == this.path; |
| 273 } | 387 } |
| 274 | 388 |
| 275 @override | 389 @override |
| 276 String readAsStringSync() { | 390 List<int> readAsBytesSync() { |
| 277 String content = _provider._pathToContent[path]; | 391 List<int> content = _provider._pathToBytes[path]; |
| 278 if (content == null) { | 392 if (content == null) { |
| 279 throw new FileSystemException(path, 'File "$path" does not exist.'); | 393 throw new FileSystemException(path, 'File "$path" does not exist.'); |
| 280 } | 394 } |
| 281 return content; | 395 return content; |
| 282 } | 396 } |
| 397 |
| 398 @override |
| 399 String readAsStringSync() { |
| 400 List<int> content = _provider._pathToBytes[path]; |
| 401 if (content == null) { |
| 402 throw new FileSystemException(path, 'File "$path" does not exist.'); |
| 403 } |
| 404 return UTF8.decode(content); |
| 405 } |
| 406 |
| 407 @override |
| 408 File renameSync(String newPath) { |
| 409 return _provider.renameFileSync(this, newPath); |
| 410 } |
| 411 |
| 412 @override |
| 413 File resolveSymbolicLinksSync() => this; |
| 414 |
| 415 @override |
| 416 void writeAsBytesSync(List<int> bytes) { |
| 417 _provider._setFileContent(this, bytes); |
| 418 } |
| 419 |
| 420 @override |
| 421 void writeAsStringSync(String content) { |
| 422 _provider._setFileContent(this, UTF8.encode(content)); |
| 423 } |
| 283 } | 424 } |
| 284 | 425 |
| 285 /** | 426 /** |
| 286 * An in-memory implementation of [Source]. | |
| 287 */ | |
| 288 class _MemoryFileSource extends Source { | |
| 289 /** | |
| 290 * Map from encoded URI/filepath pair to a unique integer identifier. This | |
| 291 * identifier is used for equality tests and hash codes. | |
| 292 * | |
| 293 * The URI and filepath are joined into a pair by separating them with an '@' | |
| 294 * character. | |
| 295 */ | |
| 296 static final Map<String, int> _idTable = new HashMap<String, int>(); | |
| 297 | |
| 298 final _MemoryFile file; | |
| 299 | |
| 300 final Uri uri; | |
| 301 | |
| 302 /** | |
| 303 * The unique ID associated with this [_MemoryFileSource]. | |
| 304 */ | |
| 305 final int id; | |
| 306 | |
| 307 _MemoryFileSource(_MemoryFile file, Uri uri) | |
| 308 : uri = uri, | |
| 309 file = file, | |
| 310 id = _idTable.putIfAbsent('$uri@${file.path}', () => _idTable.length); | |
| 311 | |
| 312 @override | |
| 313 TimestampedData<String> get contents { | |
| 314 return new TimestampedData<String>(modificationStamp, file._content); | |
| 315 } | |
| 316 | |
| 317 @override | |
| 318 String get encoding { | |
| 319 return uri.toString(); | |
| 320 } | |
| 321 | |
| 322 @override | |
| 323 String get fullName => file.path; | |
| 324 | |
| 325 @override | |
| 326 int get hashCode => id; | |
| 327 | |
| 328 @override | |
| 329 bool get isInSystemLibrary => uriKind == UriKind.DART_URI; | |
| 330 | |
| 331 @override | |
| 332 int get modificationStamp { | |
| 333 try { | |
| 334 return file.modificationStamp; | |
| 335 } on FileSystemException { | |
| 336 return -1; | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 @override | |
| 341 String get shortName => file.shortName; | |
| 342 | |
| 343 @override | |
| 344 UriKind get uriKind { | |
| 345 String scheme = uri.scheme; | |
| 346 if (scheme == PackageUriResolver.PACKAGE_SCHEME) { | |
| 347 return UriKind.PACKAGE_URI; | |
| 348 } else if (scheme == DartUriResolver.DART_SCHEME) { | |
| 349 return UriKind.DART_URI; | |
| 350 } else if (scheme == FileUriResolver.FILE_SCHEME) { | |
| 351 return UriKind.FILE_URI; | |
| 352 } | |
| 353 return UriKind.FILE_URI; | |
| 354 } | |
| 355 | |
| 356 @override | |
| 357 bool operator ==(other) { | |
| 358 return other is _MemoryFileSource && other.id == id; | |
| 359 } | |
| 360 | |
| 361 @override | |
| 362 bool exists() => file.exists; | |
| 363 | |
| 364 @override | |
| 365 Uri resolveRelativeUri(Uri relativeUri) { | |
| 366 Uri baseUri = uri; | |
| 367 String scheme = uri.scheme; | |
| 368 if (scheme == DartUriResolver.DART_SCHEME) { | |
| 369 String libraryName = uri.path; | |
| 370 baseUri = Uri.parse('$scheme:$libraryName/$libraryName.dart'); | |
| 371 } | |
| 372 return baseUri.resolveUri(relativeUri); | |
| 373 } | |
| 374 | |
| 375 @override | |
| 376 String toString() => file.toString(); | |
| 377 } | |
| 378 | |
| 379 /** | |
| 380 * An in-memory implementation of [Folder]. | 427 * An in-memory implementation of [Folder]. |
| 381 */ | 428 */ |
| 382 class _MemoryFolder extends _MemoryResource implements Folder { | 429 class _MemoryFolder extends _MemoryResource implements Folder { |
| 383 _MemoryFolder(MemoryResourceProvider provider, String path) | 430 _MemoryFolder(MemoryResourceProvider provider, String path) |
| 384 : super(provider, path); | 431 : super(provider, path); |
| 385 | 432 |
| 386 @override | 433 @override |
| 387 bool get exists => _provider._pathToResource[path] is _MemoryFolder; | 434 bool get exists => _provider._pathToResource[path] is _MemoryFolder; |
| 388 | 435 |
| 389 @override | 436 @override |
| 390 String canonicalizePath(String relPath) { | 437 String canonicalizePath(String relPath) { |
| 391 relPath = _provider.pathContext.normalize(relPath); | 438 relPath = _provider.pathContext.normalize(relPath); |
| 392 String childPath = _provider.pathContext.join(path, relPath); | 439 String childPath = _provider.pathContext.join(path, relPath); |
| 393 childPath = _provider.pathContext.normalize(childPath); | 440 childPath = _provider.pathContext.normalize(childPath); |
| 394 return childPath; | 441 return childPath; |
| 395 } | 442 } |
| 396 | 443 |
| 397 @override | 444 @override |
| 398 bool contains(String path) { | 445 bool contains(String path) { |
| 399 return _provider.pathContext.isWithin(this.path, path); | 446 return _provider.pathContext.isWithin(this.path, path); |
| 400 } | 447 } |
| 401 | 448 |
| 402 @override | 449 @override |
| 450 void delete() { |
| 451 _provider.deleteFolder(path); |
| 452 } |
| 453 |
| 454 @override |
| 403 Resource getChild(String relPath) { | 455 Resource getChild(String relPath) { |
| 404 String childPath = canonicalizePath(relPath); | 456 String childPath = canonicalizePath(relPath); |
| 405 _MemoryResource resource = _provider._pathToResource[childPath]; | 457 _MemoryResource resource = _provider._pathToResource[childPath]; |
| 406 if (resource == null) { | 458 if (resource == null) { |
| 407 resource = new _MemoryFile(_provider, childPath); | 459 resource = new _MemoryFile(_provider, childPath); |
| 408 } | 460 } |
| 409 return resource; | 461 return resource; |
| 410 } | 462 } |
| 411 | 463 |
| 412 @override | 464 @override |
| 465 _MemoryFile getChildAssumingFile(String relPath) { |
| 466 String childPath = canonicalizePath(relPath); |
| 467 _MemoryResource resource = _provider._pathToResource[childPath]; |
| 468 if (resource is _MemoryFile) { |
| 469 return resource; |
| 470 } |
| 471 return new _MemoryFile(_provider, childPath); |
| 472 } |
| 473 |
| 474 @override |
| 413 _MemoryFolder getChildAssumingFolder(String relPath) { | 475 _MemoryFolder getChildAssumingFolder(String relPath) { |
| 414 String childPath = canonicalizePath(relPath); | 476 String childPath = canonicalizePath(relPath); |
| 415 _MemoryResource resource = _provider._pathToResource[childPath]; | 477 _MemoryResource resource = _provider._pathToResource[childPath]; |
| 416 if (resource is _MemoryFolder) { | 478 if (resource is _MemoryFolder) { |
| 417 return resource; | 479 return resource; |
| 418 } | 480 } |
| 419 return new _MemoryFolder(_provider, childPath); | 481 return new _MemoryFolder(_provider, childPath); |
| 420 } | 482 } |
| 421 | 483 |
| 422 @override | 484 @override |
| (...skipping 10 matching lines...) Expand all Loading... |
| 433 return children; | 495 return children; |
| 434 } | 496 } |
| 435 | 497 |
| 436 @override | 498 @override |
| 437 bool isOrContains(String path) { | 499 bool isOrContains(String path) { |
| 438 if (path == this.path) { | 500 if (path == this.path) { |
| 439 return true; | 501 return true; |
| 440 } | 502 } |
| 441 return contains(path); | 503 return contains(path); |
| 442 } | 504 } |
| 505 |
| 506 @override |
| 507 Folder resolveSymbolicLinksSync() => this; |
| 443 } | 508 } |
| 444 | 509 |
| 445 /** | 510 /** |
| 446 * An in-memory implementation of [Resource]. | 511 * An in-memory implementation of [Resource]. |
| 447 */ | 512 */ |
| 448 abstract class _MemoryResource implements Resource { | 513 abstract class _MemoryResource implements Resource { |
| 449 final MemoryResourceProvider _provider; | 514 final MemoryResourceProvider _provider; |
| 515 @override |
| 450 final String path; | 516 final String path; |
| 451 | 517 |
| 452 _MemoryResource(this._provider, this.path); | 518 _MemoryResource(this._provider, this.path); |
| 453 | 519 |
| 454 Stream<WatchEvent> get changes { | 520 Stream<WatchEvent> get changes { |
| 455 StreamController<WatchEvent> streamController = | 521 StreamController<WatchEvent> streamController = |
| 456 new StreamController<WatchEvent>(); | 522 new StreamController<WatchEvent>(); |
| 457 if (!_provider._pathToWatchers.containsKey(path)) { | 523 if (!_provider._pathToWatchers.containsKey(path)) { |
| 458 _provider._pathToWatchers[path] = <StreamController<WatchEvent>>[]; | 524 _provider._pathToWatchers[path] = <StreamController<WatchEvent>>[]; |
| 459 } | 525 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 485 @override | 551 @override |
| 486 bool operator ==(other) { | 552 bool operator ==(other) { |
| 487 if (runtimeType != other.runtimeType) { | 553 if (runtimeType != other.runtimeType) { |
| 488 return false; | 554 return false; |
| 489 } | 555 } |
| 490 return path == other.path; | 556 return path == other.path; |
| 491 } | 557 } |
| 492 | 558 |
| 493 @override | 559 @override |
| 494 String toString() => path; | 560 String toString() => path; |
| 561 |
| 562 @override |
| 563 Uri toUri() => _provider.pathContext.toUri(path); |
| 495 } | 564 } |
| OLD | NEW |