| 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 analyzer.file_system.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:convert'; |
| 9 import 'dart:core' hide Resource; | 10 import 'dart:core' hide Resource; |
| 10 | 11 |
| 11 import 'package:analyzer/file_system/file_system.dart'; | 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:analyzer/src/source/source_resource.dart'; | 14 import 'package:analyzer/src/source/source_resource.dart'; |
| 14 import 'package:analyzer/src/util/absolute_path.dart'; | 15 import 'package:analyzer/src/util/absolute_path.dart'; |
| 15 import 'package:path/path.dart'; | 16 import 'package:path/path.dart'; |
| 16 import 'package:watcher/watcher.dart'; | 17 import 'package:watcher/watcher.dart'; |
| 17 | 18 |
| 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, List<int>> _pathToBytes = new HashMap<String, List<int>>(); |
| 27 final Map<String, int> _pathToTimestamp = new HashMap<String, int>(); | 27 final Map<String, int> _pathToTimestamp = new HashMap<String, int>(); |
| 28 final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers = | 28 final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers = |
| 29 new HashMap<String, List<StreamController<WatchEvent>>>(); | 29 new HashMap<String, List<StreamController<WatchEvent>>>(); |
| 30 int nextStamp = 0; | 30 int nextStamp = 0; |
| 31 | 31 |
| 32 final Context _pathContext; | 32 final Context _pathContext; |
| 33 @override | 33 @override |
| 34 final AbsolutePathContext absolutePathContext; | 34 final AbsolutePathContext absolutePathContext; |
| 35 | 35 |
| 36 MemoryResourceProvider({bool isWindows: false}) | 36 MemoryResourceProvider({bool isWindows: false}) |
| 37 : _pathContext = isWindows ? windows : posix, | 37 : _pathContext = isWindows ? windows : posix, |
| 38 absolutePathContext = new AbsolutePathContext(isWindows); | 38 absolutePathContext = new AbsolutePathContext(isWindows); |
| 39 | 39 |
| 40 @override | 40 @override |
| 41 Context get pathContext => _pathContext; | 41 Context get pathContext => _pathContext; |
| 42 | 42 |
| 43 /** | 43 /** |
| 44 * Delete the file with the given path. | 44 * Delete the file with the given path. |
| 45 */ | 45 */ |
| 46 void deleteFile(String path) { | 46 void deleteFile(String path) { |
| 47 _checkFileAtPath(path); | 47 _checkFileAtPath(path); |
| 48 _pathToResource.remove(path); | 48 _pathToResource.remove(path); |
| 49 _pathToContent.remove(path); | 49 _pathToBytes.remove(path); |
| 50 _pathToTimestamp.remove(path); | 50 _pathToTimestamp.remove(path); |
| 51 _notifyWatchers(path, ChangeType.REMOVE); | 51 _notifyWatchers(path, ChangeType.REMOVE); |
| 52 } | 52 } |
| 53 | 53 |
| 54 /** | 54 /** |
| 55 * Delete the folder with the given path | 55 * Delete the folder with the given path |
| 56 * and recursively delete nested files and folders. | 56 * and recursively delete nested files and folders. |
| 57 */ | 57 */ |
| 58 void deleteFolder(String path) { | 58 void deleteFolder(String path) { |
| 59 _checkFolderAtPath(path); | 59 _checkFolderAtPath(path); |
| 60 _MemoryFolder folder = _pathToResource[path]; | 60 _MemoryFolder folder = _pathToResource[path]; |
| 61 for (Resource child in folder.getChildren()) { | 61 for (Resource child in folder.getChildren()) { |
| 62 if (child is File) { | 62 if (child is File) { |
| 63 deleteFile(child.path); | 63 deleteFile(child.path); |
| 64 } else if (child is Folder) { | 64 } else if (child is Folder) { |
| 65 deleteFolder(child.path); | 65 deleteFolder(child.path); |
| 66 } else { | 66 } else { |
| 67 throw 'failed to delete resource: $child'; | 67 throw 'failed to delete resource: $child'; |
| 68 } | 68 } |
| 69 } | 69 } |
| 70 _pathToResource.remove(path); | 70 _pathToResource.remove(path); |
| 71 _pathToContent.remove(path); | 71 _pathToBytes.remove(path); |
| 72 _pathToTimestamp.remove(path); | 72 _pathToTimestamp.remove(path); |
| 73 _notifyWatchers(path, ChangeType.REMOVE); | 73 _notifyWatchers(path, ChangeType.REMOVE); |
| 74 } | 74 } |
| 75 | 75 |
| 76 @override | 76 @override |
| 77 File getFile(String path) => new _MemoryFile(this, path); | 77 File getFile(String path) => new _MemoryFile(this, path); |
| 78 | 78 |
| 79 @override | 79 @override |
| 80 Folder getFolder(String path) => newFolder(path); | 80 Folder getFolder(String path) => newFolder(path); |
| 81 | 81 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 97 return resource; | 97 return resource; |
| 98 } | 98 } |
| 99 | 99 |
| 100 @override | 100 @override |
| 101 Folder getStateLocation(String pluginId) { | 101 Folder getStateLocation(String pluginId) { |
| 102 return newFolder('/user/home/$pluginId'); | 102 return newFolder('/user/home/$pluginId'); |
| 103 } | 103 } |
| 104 | 104 |
| 105 void modifyFile(String path, String content) { | 105 void modifyFile(String path, String content) { |
| 106 _checkFileAtPath(path); | 106 _checkFileAtPath(path); |
| 107 _pathToContent[path] = content; | 107 _pathToBytes[path] = UTF8.encode(content); |
| 108 _pathToTimestamp[path] = nextStamp++; | 108 _pathToTimestamp[path] = nextStamp++; |
| 109 _notifyWatchers(path, ChangeType.MODIFY); | 109 _notifyWatchers(path, ChangeType.MODIFY); |
| 110 } | 110 } |
| 111 | 111 |
| 112 /** | 112 /** |
| 113 * Create a resource representing a dummy link (that is, a File object which | 113 * Create a resource representing a dummy link (that is, a File object which |
| 114 * appears in its parent directory, but whose `exists` property is false) | 114 * appears in its parent directory, but whose `exists` property is false) |
| 115 */ | 115 */ |
| 116 File newDummyLink(String path) { | 116 File newDummyLink(String path) { |
| 117 path = pathContext.normalize(path); | 117 path = pathContext.normalize(path); |
| 118 newFolder(pathContext.dirname(path)); | 118 newFolder(pathContext.dirname(path)); |
| 119 _MemoryDummyLink link = new _MemoryDummyLink(this, path); | 119 _MemoryDummyLink link = new _MemoryDummyLink(this, path); |
| 120 _pathToResource[path] = link; | 120 _pathToResource[path] = link; |
| 121 _pathToTimestamp[path] = nextStamp++; | 121 _pathToTimestamp[path] = nextStamp++; |
| 122 _notifyWatchers(path, ChangeType.ADD); | 122 _notifyWatchers(path, ChangeType.ADD); |
| 123 return link; | 123 return link; |
| 124 } | 124 } |
| 125 | 125 |
| 126 File newFile(String path, String content, [int stamp]) { | 126 File newFile(String path, String content, [int stamp]) { |
| 127 path = pathContext.normalize(path); | 127 path = pathContext.normalize(path); |
| 128 _MemoryFile file = _newFile(path); | 128 _MemoryFile file = _newFile(path); |
| 129 _pathToContent[path] = content; | 129 _pathToBytes[path] = UTF8.encode(content); |
| 130 _pathToTimestamp[path] = stamp ?? nextStamp++; | 130 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| 131 _notifyWatchers(path, ChangeType.ADD); | 131 _notifyWatchers(path, ChangeType.ADD); |
| 132 return file; | 132 return file; |
| 133 } | 133 } |
| 134 | 134 |
| 135 File newFileWithBytes(String path, List<int> bytes, [int stamp]) { | 135 File newFileWithBytes(String path, List<int> bytes, [int stamp]) { |
| 136 path = pathContext.normalize(path); | 136 path = pathContext.normalize(path); |
| 137 _MemoryFile file = _newFile(path); | 137 _MemoryFile file = _newFile(path); |
| 138 _pathToBytes[path] = bytes; | 138 _pathToBytes[path] = bytes; |
| 139 _pathToTimestamp[path] = stamp ?? nextStamp++; | 139 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 if (newPath == path) { | 172 if (newPath == path) { |
| 173 return file; | 173 return file; |
| 174 } | 174 } |
| 175 _MemoryResource existingNewResource = _pathToResource[newPath]; | 175 _MemoryResource existingNewResource = _pathToResource[newPath]; |
| 176 if (existingNewResource is _MemoryFolder) { | 176 if (existingNewResource is _MemoryFolder) { |
| 177 throw new FileSystemException( | 177 throw new FileSystemException( |
| 178 path, 'Could not be renamed: $newPath is a folder.'); | 178 path, 'Could not be renamed: $newPath is a folder.'); |
| 179 } | 179 } |
| 180 _MemoryFile newFile = _newFile(newPath); | 180 _MemoryFile newFile = _newFile(newPath); |
| 181 _pathToResource.remove(path); | 181 _pathToResource.remove(path); |
| 182 _pathToContent[newPath] = _pathToContent.remove(path); | |
| 183 _pathToBytes[newPath] = _pathToBytes.remove(path); | 182 _pathToBytes[newPath] = _pathToBytes.remove(path); |
| 184 _pathToTimestamp[newPath] = _pathToTimestamp.remove(path); | 183 _pathToTimestamp[newPath] = _pathToTimestamp.remove(path); |
| 185 if (existingNewResource != null) { | 184 if (existingNewResource != null) { |
| 186 _notifyWatchers(newPath, ChangeType.REMOVE); | 185 _notifyWatchers(newPath, ChangeType.REMOVE); |
| 187 } | 186 } |
| 188 _notifyWatchers(path, ChangeType.REMOVE); | 187 _notifyWatchers(path, ChangeType.REMOVE); |
| 189 _notifyWatchers(newPath, ChangeType.ADD); | 188 _notifyWatchers(newPath, ChangeType.ADD); |
| 190 return newFile; | 189 return newFile; |
| 191 } | 190 } |
| 192 | 191 |
| 193 File updateFile(String path, String content, [int stamp]) { | 192 File updateFile(String path, String content, [int stamp]) { |
| 194 path = pathContext.normalize(path); | 193 path = pathContext.normalize(path); |
| 195 newFolder(pathContext.dirname(path)); | 194 newFolder(pathContext.dirname(path)); |
| 196 _MemoryFile file = new _MemoryFile(this, path); | 195 _MemoryFile file = new _MemoryFile(this, path); |
| 197 _pathToResource[path] = file; | 196 _pathToResource[path] = file; |
| 198 _pathToContent[path] = content; | 197 _pathToBytes[path] = UTF8.encode(content); |
| 199 _pathToTimestamp[path] = stamp ?? nextStamp++; | 198 _pathToTimestamp[path] = stamp ?? nextStamp++; |
| 200 _notifyWatchers(path, ChangeType.MODIFY); | 199 _notifyWatchers(path, ChangeType.MODIFY); |
| 201 return file; | 200 return file; |
| 202 } | 201 } |
| 203 | 202 |
| 204 void _checkFileAtPath(String path) { | 203 void _checkFileAtPath(String path) { |
| 205 _MemoryResource resource = _pathToResource[path]; | 204 _MemoryResource resource = _pathToResource[path]; |
| 206 if (resource is! _MemoryFile) { | 205 if (resource is! _MemoryFile) { |
| 207 throw new ArgumentError( | 206 throw new ArgumentError( |
| 208 'File expected at "$path" but ${resource.runtimeType} found'); | 207 'File expected at "$path" but ${resource.runtimeType} found'); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 238 List<StreamController<WatchEvent>> streamControllers) { | 237 List<StreamController<WatchEvent>> streamControllers) { |
| 239 if (watcherPath == path || pathContext.isWithin(watcherPath, path)) { | 238 if (watcherPath == path || pathContext.isWithin(watcherPath, path)) { |
| 240 for (StreamController<WatchEvent> streamController | 239 for (StreamController<WatchEvent> streamController |
| 241 in streamControllers) { | 240 in streamControllers) { |
| 242 streamController.add(new WatchEvent(changeType, path)); | 241 streamController.add(new WatchEvent(changeType, path)); |
| 243 } | 242 } |
| 244 } | 243 } |
| 245 }); | 244 }); |
| 246 } | 245 } |
| 247 | 246 |
| 248 void _setFileBytes(_MemoryFile file, List<int> bytes) { | 247 void _setFileContent(_MemoryFile file, List<int> bytes) { |
| 249 String path = file.path; | 248 String path = file.path; |
| 250 _pathToResource[path] = file; | 249 _pathToResource[path] = file; |
| 251 _pathToBytes[path] = bytes; | 250 _pathToBytes[path] = bytes; |
| 252 _pathToTimestamp[path] = nextStamp++; | 251 _pathToTimestamp[path] = nextStamp++; |
| 253 _notifyWatchers(path, ChangeType.MODIFY); | 252 _notifyWatchers(path, ChangeType.MODIFY); |
| 254 } | 253 } |
| 255 } | 254 } |
| 256 | 255 |
| 257 /** | 256 /** |
| 258 * An in-memory implementation of [File] which acts like a symbolic link to a | 257 * An in-memory implementation of [File] which acts like a symbolic link to a |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 throw new FileSystemException(path, 'File could not be renamed'); | 308 throw new FileSystemException(path, 'File could not be renamed'); |
| 310 } | 309 } |
| 311 | 310 |
| 312 @override | 311 @override |
| 313 Uri toUri() => new Uri.file(path, windows: _provider.pathContext == windows); | 312 Uri toUri() => new Uri.file(path, windows: _provider.pathContext == windows); |
| 314 | 313 |
| 315 @override | 314 @override |
| 316 void writeAsBytesSync(List<int> bytes) { | 315 void writeAsBytesSync(List<int> bytes) { |
| 317 throw new FileSystemException(path, 'File could not be written'); | 316 throw new FileSystemException(path, 'File could not be written'); |
| 318 } | 317 } |
| 318 |
| 319 @override |
| 320 void writeAsStringSync(String content) { |
| 321 throw new FileSystemException(path, 'File could not be written'); |
| 322 } |
| 319 } | 323 } |
| 320 | 324 |
| 321 /** | 325 /** |
| 322 * An in-memory implementation of [File]. | 326 * An in-memory implementation of [File]. |
| 323 */ | 327 */ |
| 324 class _MemoryFile extends _MemoryResource implements File { | 328 class _MemoryFile extends _MemoryResource implements File { |
| 325 _MemoryFile(MemoryResourceProvider provider, String path) | 329 _MemoryFile(MemoryResourceProvider provider, String path) |
| 326 : super(provider, path); | 330 : super(provider, path); |
| 327 | 331 |
| 328 @override | 332 @override |
| (...skipping 19 matching lines...) Expand all Loading... |
| 348 _provider.deleteFile(path); | 352 _provider.deleteFile(path); |
| 349 } | 353 } |
| 350 | 354 |
| 351 @override | 355 @override |
| 352 bool isOrContains(String path) { | 356 bool isOrContains(String path) { |
| 353 return path == this.path; | 357 return path == this.path; |
| 354 } | 358 } |
| 355 | 359 |
| 356 @override | 360 @override |
| 357 List<int> readAsBytesSync() { | 361 List<int> readAsBytesSync() { |
| 358 List<int> bytes = _provider._pathToBytes[path]; | 362 List<int> content = _provider._pathToBytes[path]; |
| 359 if (bytes == null) { | |
| 360 throw new FileSystemException(path, 'File "$path" is not binary.'); | |
| 361 } | |
| 362 return bytes; | |
| 363 } | |
| 364 | |
| 365 @override | |
| 366 String readAsStringSync() { | |
| 367 String content = _provider._pathToContent[path]; | |
| 368 if (content == null) { | 363 if (content == null) { |
| 369 throw new FileSystemException(path, 'File "$path" does not exist.'); | 364 throw new FileSystemException(path, 'File "$path" does not exist.'); |
| 370 } | 365 } |
| 371 return content; | 366 return content; |
| 372 } | 367 } |
| 373 | 368 |
| 374 @override | 369 @override |
| 370 String readAsStringSync() { |
| 371 List<int> content = _provider._pathToBytes[path]; |
| 372 if (content == null) { |
| 373 throw new FileSystemException(path, 'File "$path" does not exist.'); |
| 374 } |
| 375 return UTF8.decode(content); |
| 376 } |
| 377 |
| 378 @override |
| 375 File renameSync(String newPath) { | 379 File renameSync(String newPath) { |
| 376 return _provider.renameFileSync(this, newPath); | 380 return _provider.renameFileSync(this, newPath); |
| 377 } | 381 } |
| 378 | 382 |
| 379 @override | 383 @override |
| 380 Uri toUri() => new Uri.file(path, windows: _provider.pathContext == windows); | 384 Uri toUri() => new Uri.file(path, windows: _provider.pathContext == windows); |
| 381 | 385 |
| 382 @override | 386 @override |
| 383 void writeAsBytesSync(List<int> bytes) { | 387 void writeAsBytesSync(List<int> bytes) { |
| 384 _provider._setFileBytes(this, bytes); | 388 _provider._setFileContent(this, bytes); |
| 389 } |
| 390 |
| 391 @override |
| 392 void writeAsStringSync(String content) { |
| 393 _provider._setFileContent(this, UTF8.encode(content)); |
| 385 } | 394 } |
| 386 } | 395 } |
| 387 | 396 |
| 388 /** | 397 /** |
| 389 * An in-memory implementation of [Folder]. | 398 * An in-memory implementation of [Folder]. |
| 390 */ | 399 */ |
| 391 class _MemoryFolder extends _MemoryResource implements Folder { | 400 class _MemoryFolder extends _MemoryResource implements Folder { |
| 392 _MemoryFolder(MemoryResourceProvider provider, String path) | 401 _MemoryFolder(MemoryResourceProvider provider, String path) |
| 393 : super(provider, path); | 402 : super(provider, path); |
| 394 | 403 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 515 bool operator ==(other) { | 524 bool operator ==(other) { |
| 516 if (runtimeType != other.runtimeType) { | 525 if (runtimeType != other.runtimeType) { |
| 517 return false; | 526 return false; |
| 518 } | 527 } |
| 519 return path == other.path; | 528 return path == other.path; |
| 520 } | 529 } |
| 521 | 530 |
| 522 @override | 531 @override |
| 523 String toString() => path; | 532 String toString() => path; |
| 524 } | 533 } |
| OLD | NEW |