| Index: mojo/public/dart/third_party/analyzer/lib/file_system/memory_file_system.dart
|
| diff --git a/mojo/public/dart/third_party/analyzer/lib/file_system/memory_file_system.dart b/mojo/public/dart/third_party/analyzer/lib/file_system/memory_file_system.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1684fe969911a74677be2198c9bd97543419d585
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/analyzer/lib/file_system/memory_file_system.dart
|
| @@ -0,0 +1,489 @@
|
| +// Copyright (c) 2014, 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.
|
| +
|
| +library memory_file_system;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:collection';
|
| +import 'dart:core' hide Resource;
|
| +
|
| +import 'package:analyzer/src/generated/engine.dart' show TimestampedData;
|
| +import 'package:analyzer/src/generated/source_io.dart';
|
| +import 'package:path/path.dart';
|
| +import 'package:watcher/watcher.dart';
|
| +
|
| +import 'file_system.dart';
|
| +
|
| +/**
|
| + * An in-memory implementation of [ResourceProvider].
|
| + * Use `/` as a path separator.
|
| + */
|
| +class MemoryResourceProvider implements ResourceProvider {
|
| + final Map<String, _MemoryResource> _pathToResource =
|
| + new HashMap<String, _MemoryResource>();
|
| + final Map<String, String> _pathToContent = new HashMap<String, String>();
|
| + final Map<String, int> _pathToTimestamp = new HashMap<String, int>();
|
| + final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers =
|
| + new HashMap<String, List<StreamController<WatchEvent>>>();
|
| + int nextStamp = 0;
|
| +
|
| + @override
|
| + Context get pathContext => posix;
|
| +
|
| + /**
|
| + * Delete the file with the given path.
|
| + */
|
| + void deleteFile(String path) {
|
| + _checkFileAtPath(path);
|
| + _pathToResource.remove(path);
|
| + _pathToContent.remove(path);
|
| + _pathToTimestamp.remove(path);
|
| + _notifyWatchers(path, ChangeType.REMOVE);
|
| + }
|
| +
|
| + /**
|
| + * Delete the folder with the given path
|
| + * and recurively delete nested files and folders.
|
| + */
|
| + void deleteFolder(String path) {
|
| + _checkFolderAtPath(path);
|
| + _MemoryFolder folder = _pathToResource[path];
|
| + for (Resource child in folder.getChildren()) {
|
| + if (child is File) {
|
| + deleteFile(child.path);
|
| + } else if (child is Folder) {
|
| + deleteFolder(child.path);
|
| + } else {
|
| + throw 'failed to delete resource: $child';
|
| + }
|
| + }
|
| + _pathToResource.remove(path);
|
| + _pathToContent.remove(path);
|
| + _pathToTimestamp.remove(path);
|
| + _notifyWatchers(path, ChangeType.REMOVE);
|
| + }
|
| +
|
| + @override
|
| + File getFile(String path) => new _MemoryFile(this, path);
|
| +
|
| + @override
|
| + Folder getFolder(String path) => newFolder(path);
|
| +
|
| + @override
|
| + Resource getResource(String path) {
|
| + path = pathContext.normalize(path);
|
| + Resource resource = _pathToResource[path];
|
| + if (resource == null) {
|
| + resource = new _MemoryFile(this, path);
|
| + }
|
| + return resource;
|
| + }
|
| +
|
| + @override
|
| + Folder getStateLocation(String pluginId) {
|
| + return newFolder('/user/home/$pluginId');
|
| + }
|
| +
|
| + void modifyFile(String path, String content) {
|
| + _checkFileAtPath(path);
|
| + _pathToContent[path] = content;
|
| + _pathToTimestamp[path] = nextStamp++;
|
| + _notifyWatchers(path, ChangeType.MODIFY);
|
| + }
|
| +
|
| + /**
|
| + * Create a resource representing a dummy link (that is, a File object which
|
| + * appears in its parent directory, but whose `exists` property is false)
|
| + */
|
| + File newDummyLink(String path) {
|
| + path = pathContext.normalize(path);
|
| + newFolder(pathContext.dirname(path));
|
| + _MemoryDummyLink link = new _MemoryDummyLink(this, path);
|
| + _pathToResource[path] = link;
|
| + _pathToTimestamp[path] = nextStamp++;
|
| + _notifyWatchers(path, ChangeType.ADD);
|
| + return link;
|
| + }
|
| +
|
| + File newFile(String path, String content, [int stamp]) {
|
| + path = pathContext.normalize(path);
|
| + _MemoryResource folder = _pathToResource[pathContext.dirname(path)];
|
| + if (folder == null) {
|
| + newFolder(pathContext.dirname(path));
|
| + } else if (folder is! Folder) {
|
| + throw new ArgumentError('Cannot create file ($path) as child of file');
|
| + }
|
| + _MemoryFile file = new _MemoryFile(this, path);
|
| + _pathToResource[path] = file;
|
| + _pathToContent[path] = content;
|
| + _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
|
| + _notifyWatchers(path, ChangeType.ADD);
|
| + return file;
|
| + }
|
| +
|
| + Folder newFolder(String path) {
|
| + path = pathContext.normalize(path);
|
| + if (!path.startsWith(pathContext.separator)) {
|
| + throw new ArgumentError(
|
| + "Path must start with '${pathContext.separator}' : $path");
|
| + }
|
| + _MemoryResource resource = _pathToResource[path];
|
| + if (resource == null) {
|
| + String parentPath = pathContext.dirname(path);
|
| + if (parentPath != path) {
|
| + newFolder(parentPath);
|
| + }
|
| + _MemoryFolder folder = new _MemoryFolder(this, path);
|
| + _pathToResource[path] = folder;
|
| + _pathToTimestamp[path] = nextStamp++;
|
| + _notifyWatchers(path, ChangeType.ADD);
|
| + return folder;
|
| + } else if (resource is _MemoryFolder) {
|
| + _notifyWatchers(path, ChangeType.ADD);
|
| + return resource;
|
| + } else {
|
| + String message =
|
| + 'Folder expected at ' "'$path'" 'but ${resource.runtimeType} found';
|
| + throw new ArgumentError(message);
|
| + }
|
| + }
|
| +
|
| + File updateFile(String path, String content, [int stamp]) {
|
| + path = pathContext.normalize(path);
|
| + newFolder(pathContext.dirname(path));
|
| + _MemoryFile file = new _MemoryFile(this, path);
|
| + _pathToResource[path] = file;
|
| + _pathToContent[path] = content;
|
| + _pathToTimestamp[path] = stamp != null ? stamp : nextStamp++;
|
| + _notifyWatchers(path, ChangeType.MODIFY);
|
| + return file;
|
| + }
|
| +
|
| + void _checkFileAtPath(String path) {
|
| + _MemoryResource resource = _pathToResource[path];
|
| + if (resource is! _MemoryFile) {
|
| + throw new ArgumentError(
|
| + 'File expected at "$path" but ${resource.runtimeType} found');
|
| + }
|
| + }
|
| +
|
| + void _checkFolderAtPath(String path) {
|
| + _MemoryResource resource = _pathToResource[path];
|
| + if (resource is! _MemoryFolder) {
|
| + throw new ArgumentError(
|
| + 'Folder expected at "$path" but ${resource.runtimeType} found');
|
| + }
|
| + }
|
| +
|
| + void _notifyWatchers(String path, ChangeType changeType) {
|
| + _pathToWatchers.forEach((String watcherPath,
|
| + List<StreamController<WatchEvent>> streamControllers) {
|
| + if (watcherPath == path || pathContext.isWithin(watcherPath, path)) {
|
| + for (StreamController<WatchEvent> streamController
|
| + in streamControllers) {
|
| + streamController.add(new WatchEvent(changeType, path));
|
| + }
|
| + }
|
| + });
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * An in-memory implementation of [File] which acts like a symbolic link to a
|
| + * non-existent file.
|
| + */
|
| +class _MemoryDummyLink extends _MemoryResource implements File {
|
| + _MemoryDummyLink(MemoryResourceProvider provider, String path)
|
| + : super(provider, path);
|
| +
|
| + @override
|
| + Stream<WatchEvent> get changes {
|
| + throw new FileSystemException(path, "File does not exist");
|
| + }
|
| +
|
| + @override
|
| + bool get exists => false;
|
| +
|
| + int get modificationStamp {
|
| + int stamp = _provider._pathToTimestamp[path];
|
| + if (stamp == null) {
|
| + throw new FileSystemException(path, "File does not exist");
|
| + }
|
| + return stamp;
|
| + }
|
| +
|
| + String get _content {
|
| + throw new FileSystemException(path, 'File could not be read');
|
| + }
|
| +
|
| + @override
|
| + Source createSource([Uri uri]) {
|
| + throw new FileSystemException(path, 'File could not be read');
|
| + }
|
| +
|
| + @override
|
| + bool isOrContains(String path) {
|
| + return path == this.path;
|
| + }
|
| +
|
| + @override
|
| + String readAsStringSync() {
|
| + throw new FileSystemException(path, 'File could not be read');
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * An in-memory implementation of [File].
|
| + */
|
| +class _MemoryFile extends _MemoryResource implements File {
|
| + _MemoryFile(MemoryResourceProvider provider, String path)
|
| + : super(provider, path);
|
| +
|
| + @override
|
| + bool get exists => _provider._pathToResource[path] is _MemoryFile;
|
| +
|
| + int get modificationStamp {
|
| + int stamp = _provider._pathToTimestamp[path];
|
| + if (stamp == null) {
|
| + throw new FileSystemException(path, 'File "$path" does not exist.');
|
| + }
|
| + return stamp;
|
| + }
|
| +
|
| + String get _content {
|
| + String content = _provider._pathToContent[path];
|
| + if (content == null) {
|
| + throw new FileSystemException(path, 'File "$path" does not exist.');
|
| + }
|
| + return content;
|
| + }
|
| +
|
| + @override
|
| + Source createSource([Uri uri]) {
|
| + if (uri == null) {
|
| + uri = _provider.pathContext.toUri(path);
|
| + }
|
| + return new _MemoryFileSource(this, uri);
|
| + }
|
| +
|
| + @override
|
| + bool isOrContains(String path) {
|
| + return path == this.path;
|
| + }
|
| +
|
| + @override
|
| + String readAsStringSync() {
|
| + String content = _provider._pathToContent[path];
|
| + if (content == null) {
|
| + throw new FileSystemException(path, 'File "$path" does not exist.');
|
| + }
|
| + return content;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * An in-memory implementation of [Source].
|
| + */
|
| +class _MemoryFileSource extends Source {
|
| + /**
|
| + * Map from encoded URI/filepath pair to a unique integer identifier. This
|
| + * identifier is used for equality tests and hash codes.
|
| + *
|
| + * The URI and filepath are joined into a pair by separating them with an '@'
|
| + * character.
|
| + */
|
| + static final Map<String, int> _idTable = new HashMap<String, int>();
|
| +
|
| + final _MemoryFile file;
|
| +
|
| + final Uri uri;
|
| +
|
| + /**
|
| + * The unique ID associated with this [_MemoryFileSource].
|
| + */
|
| + final int id;
|
| +
|
| + _MemoryFileSource(_MemoryFile file, Uri uri)
|
| + : uri = uri,
|
| + file = file,
|
| + id = _idTable.putIfAbsent('$uri@${file.path}', () => _idTable.length);
|
| +
|
| + @override
|
| + TimestampedData<String> get contents {
|
| + return new TimestampedData<String>(modificationStamp, file._content);
|
| + }
|
| +
|
| + @override
|
| + String get encoding {
|
| + return uri.toString();
|
| + }
|
| +
|
| + @override
|
| + String get fullName => file.path;
|
| +
|
| + @override
|
| + int get hashCode => id;
|
| +
|
| + @override
|
| + bool get isInSystemLibrary => uriKind == UriKind.DART_URI;
|
| +
|
| + @override
|
| + int get modificationStamp {
|
| + try {
|
| + return file.modificationStamp;
|
| + } on FileSystemException {
|
| + return -1;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + String get shortName => file.shortName;
|
| +
|
| + @override
|
| + UriKind get uriKind {
|
| + String scheme = uri.scheme;
|
| + if (scheme == PackageUriResolver.PACKAGE_SCHEME) {
|
| + return UriKind.PACKAGE_URI;
|
| + } else if (scheme == DartUriResolver.DART_SCHEME) {
|
| + return UriKind.DART_URI;
|
| + } else if (scheme == FileUriResolver.FILE_SCHEME) {
|
| + return UriKind.FILE_URI;
|
| + }
|
| + return UriKind.FILE_URI;
|
| + }
|
| +
|
| + @override
|
| + bool operator ==(other) {
|
| + return other is _MemoryFileSource && other.id == id;
|
| + }
|
| +
|
| + @override
|
| + bool exists() => file.exists;
|
| +
|
| + @override
|
| + Uri resolveRelativeUri(Uri relativeUri) {
|
| + return uri.resolveUri(relativeUri);
|
| + }
|
| +
|
| + @override
|
| + String toString() => file.toString();
|
| +}
|
| +
|
| +/**
|
| + * An in-memory implementation of [Folder].
|
| + */
|
| +class _MemoryFolder extends _MemoryResource implements Folder {
|
| + _MemoryFolder(MemoryResourceProvider provider, String path)
|
| + : super(provider, path);
|
| +
|
| + @override
|
| + bool get exists => _provider._pathToResource[path] is _MemoryFolder;
|
| +
|
| + @override
|
| + String canonicalizePath(String relPath) {
|
| + relPath = _provider.pathContext.normalize(relPath);
|
| + String childPath = _provider.pathContext.join(path, relPath);
|
| + childPath = _provider.pathContext.normalize(childPath);
|
| + return childPath;
|
| + }
|
| +
|
| + @override
|
| + bool contains(String path) {
|
| + return _provider.pathContext.isWithin(this.path, path);
|
| + }
|
| +
|
| + @override
|
| + Resource getChild(String relPath) {
|
| + String childPath = canonicalizePath(relPath);
|
| + _MemoryResource resource = _provider._pathToResource[childPath];
|
| + if (resource == null) {
|
| + resource = new _MemoryFile(_provider, childPath);
|
| + }
|
| + return resource;
|
| + }
|
| +
|
| + @override
|
| + _MemoryFolder getChildAssumingFolder(String relPath) {
|
| + String childPath = canonicalizePath(relPath);
|
| + _MemoryResource resource = _provider._pathToResource[childPath];
|
| + if (resource is _MemoryFolder) {
|
| + return resource;
|
| + }
|
| + return new _MemoryFolder(_provider, childPath);
|
| + }
|
| +
|
| + @override
|
| + List<Resource> getChildren() {
|
| + if (!exists) {
|
| + throw new FileSystemException(path, 'Folder does not exist.');
|
| + }
|
| + List<Resource> children = <Resource>[];
|
| + _provider._pathToResource.forEach((resourcePath, resource) {
|
| + if (_provider.pathContext.dirname(resourcePath) == path) {
|
| + children.add(resource);
|
| + }
|
| + });
|
| + return children;
|
| + }
|
| +
|
| + @override
|
| + bool isOrContains(String path) {
|
| + if (path == this.path) {
|
| + return true;
|
| + }
|
| + return contains(path);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * An in-memory implementation of [Resource].
|
| + */
|
| +abstract class _MemoryResource implements Resource {
|
| + final MemoryResourceProvider _provider;
|
| + final String path;
|
| +
|
| + _MemoryResource(this._provider, this.path);
|
| +
|
| + Stream<WatchEvent> get changes {
|
| + StreamController<WatchEvent> streamController =
|
| + new StreamController<WatchEvent>();
|
| + if (!_provider._pathToWatchers.containsKey(path)) {
|
| + _provider._pathToWatchers[path] = <StreamController<WatchEvent>>[];
|
| + }
|
| + _provider._pathToWatchers[path].add(streamController);
|
| + streamController.done.then((_) {
|
| + _provider._pathToWatchers[path].remove(streamController);
|
| + if (_provider._pathToWatchers[path].isEmpty) {
|
| + _provider._pathToWatchers.remove(path);
|
| + }
|
| + });
|
| + return streamController.stream;
|
| + }
|
| +
|
| + @override
|
| + get hashCode => path.hashCode;
|
| +
|
| + @override
|
| + Folder get parent {
|
| + String parentPath = _provider.pathContext.dirname(path);
|
| + if (parentPath == path) {
|
| + return null;
|
| + }
|
| + return _provider.getResource(parentPath);
|
| + }
|
| +
|
| + @override
|
| + String get shortName => _provider.pathContext.basename(path);
|
| +
|
| + @override
|
| + bool operator ==(other) {
|
| + if (runtimeType != other.runtimeType) {
|
| + return false;
|
| + }
|
| + return path == other.path;
|
| + }
|
| +
|
| + @override
|
| + String toString() => path;
|
| +}
|
|
|