Chromium Code Reviews| Index: pkg/analysis_server/lib/src/resource.dart |
| diff --git a/pkg/analysis_server/lib/src/resource.dart b/pkg/analysis_server/lib/src/resource.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fda8b41a654ad1dd448aa0e020487f787d5ea80c |
| --- /dev/null |
| +++ b/pkg/analysis_server/lib/src/resource.dart |
| @@ -0,0 +1,359 @@ |
| +// 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 resource; |
| + |
| +import 'dart:io' as io; |
| + |
| +import 'package:analyzer/src/generated/engine.dart' show TimestampedData; |
| +import 'package:analyzer/src/generated/java_io.dart'; |
| +import 'package:analyzer/src/generated/source_io.dart'; |
| +import 'package:path/path.dart'; |
| + |
| + |
| +/** |
| + * [File]s are leaf [Resource]s which contain data. |
| + */ |
| +abstract class File extends Resource { |
| + /** |
| + * Create a new [Source] instance that serves this file. |
| + */ |
| + Source createSource(UriKind uriKind); |
| +} |
| + |
| + |
| +/** |
| + * [Folder]s are [Resource]s which may contain files and/or other folders. |
| + */ |
| +abstract class Folder extends Resource { |
| + /** |
| + * Return an existing child [Resource] with the given [relPath]. |
| + * Return a not existing [File] if no such child exist. |
| + */ |
| + Resource getChild(String relPath); |
| + |
| + /** |
| + * Return a list of existing direct children [Resource]s (folders and files) |
| + * in this folder, in no particular order. |
| + */ |
| + List<Resource> getChildren(); |
| +} |
| + |
| + |
| +/** |
| + * The abstract class [Resource] is an abstraction of file or folder. |
| + */ |
| +abstract class Resource { |
| + /** |
| + * Return `true` if this resource exists. |
| + */ |
| + bool get exists; |
| + |
| + /** |
| + * Return the full (long) version of the name that can be displayed to the |
| + * user to denote this resource. |
| + */ |
| + String get fullName; |
| + |
| + /** |
| + * Return a short version of the name that can be displayed to the user to |
| + * denote this resource. |
| + */ |
| + String get shortName; |
| +} |
| + |
| + |
| +/** |
| + * Instances of the class [ResourceProvider] convert [String] paths into |
| + * [Resource]s. |
| + */ |
| +abstract class ResourceProvider { |
| + /** |
| + * Return the [Resource] that corresponds to the given [path]. |
| + */ |
| + Resource getResource(String path); |
| +} |
| + |
| + |
| +/** |
| + * An in-memory implementation of [Resource]. |
| + */ |
| +abstract class _MemoryResource implements Resource { |
| + final MemoryResourceProvider _provider; |
| + final String _path; |
| + |
| + _MemoryResource(this._provider, this._path); |
| + |
| + @override |
| + bool operator ==(o) { |
|
Brian Wilkerson
2014/05/25 15:29:58
I really dislike single character variable names f
scheglov
2014/05/25 16:16:38
Done.
|
| + return identical(this, o); |
| + } |
| + |
| + @override |
| + bool get exists => _provider._pathToResource.containsKey(_path); |
| + |
| + @override |
| + String get fullName => _path; |
| + |
| + @override |
| + get hashCode => _path.hashCode; |
| + |
| + @override |
| + String get shortName => basename(_path); |
| + |
| + @override |
| + String toString() => fullName; |
| +} |
| + |
| + |
| +/** |
| + * An in-memory implementation of [File]. |
| + */ |
| +class _MemoryFile extends _MemoryResource implements File { |
| + _MemoryFile(MemoryResourceProvider provider, String path) : |
| + super(provider, path); |
| + |
| + @override |
| + Source createSource(UriKind uriKind) { |
| + return new _MemoryFileSource(this, uriKind); |
| + } |
| + |
| + String get _content => _provider._pathToContent[_path]; |
| + |
| + int get _timestamp => _provider._pathToTimestamp[_path]; |
| +} |
| + |
| + |
| +/** |
| + * An in-memory implementation of [Source]. |
| + */ |
| +class _MemoryFileSource implements Source { |
| + final _MemoryFile _file; |
| + |
| + final UriKind uriKind; |
| + |
| + _MemoryFileSource(this._file, this.uriKind); |
| + |
| + @override |
| + TimestampedData<String> get contents { |
| + return new TimestampedData<String>(modificationStamp, _file._content); |
| + } |
| + |
| + @override |
| + String get encoding { |
| + return '${new String.fromCharCode(uriKind.encoding)}${_file.fullName}'; |
| + } |
| + |
| + @override |
| + bool exists() => _file.exists; |
| + |
| + @override |
| + String get fullName => _file.fullName; |
| + |
| + @override |
| + bool get isInSystemLibrary => false; |
| + |
| + @override |
| + int get modificationStamp => _file._timestamp; |
| + |
| + @override |
| + Source resolveRelative(Uri relativeUri) { |
| + String relativePath = fromUri(relativeUri); |
| + String folderPath = dirname(_file._path); |
| + String path = join(folderPath, relativePath); |
| + path = normalize(path); |
| + _MemoryFile file = new _MemoryFile(_file._provider, path); |
| + return new _MemoryFileSource(file, uriKind); |
| + } |
| + |
| + @override |
| + String get shortName => _file.shortName; |
| +} |
| + |
| + |
| +/** |
| + * An in-memory implementation of [Folder]. |
| + */ |
| +class _MemoryFolder extends _MemoryResource implements Folder { |
| + _MemoryFolder(MemoryResourceProvider provider, String path) : |
| + super(provider, path); |
| + @override |
| + Resource getChild(String relPath) { |
| + relPath = normalize(relPath); |
| + String childPath = join(_path, relPath); |
| + childPath = normalize(childPath); |
| + _MemoryResource resource = _provider._pathToResource[childPath]; |
| + if (resource == null) { |
| + resource = new _MemoryFile(_provider, childPath); |
| + } |
| + return resource; |
| + } |
| + |
| + @override |
| + List<Resource> getChildren() { |
| + List<Resource> children = []; |
|
Brian Wilkerson
2014/05/25 15:29:58
I think we should include a type annotation with l
scheglov
2014/05/25 16:16:38
Done.
|
| + _provider._pathToResource.forEach((path, resource) { |
| + if (dirname(path) == _path) { |
| + children.add(resource); |
| + } |
| + }); |
| + return children; |
| + } |
| +} |
| + |
| + |
| +/** |
| + * An in-memory implementation of [ResourceProvider]. |
| + * Use `/` as a path separator. |
| + */ |
| +class MemoryResourceProvider implements ResourceProvider { |
| + final Map<String, _MemoryResource> _pathToResource = {}; |
| + final Map<String, String> _pathToContent = {}; |
| + final Map<String, int> _pathToTimestamp = {}; |
| + int nextStamp = 0; |
| + |
| + @override |
| + Resource getResource(String path) { |
| + path = normalize(path); |
| + Resource resource = _pathToResource[path]; |
| + if (resource == null) { |
| + resource = new _MemoryFile(this, path); |
| + } |
| + return resource; |
| + } |
| + |
| + Folder newFolder(String path) { |
| + path = normalize(path); |
| + if (path.isEmpty) { |
| + throw new ArgumentError('Empty paths are not supported'); |
| + } |
| + if (!path.startsWith('/')) { |
| + throw new ArgumentError('Path must start with \'/\''); |
|
Brian Wilkerson
2014/05/25 15:29:58
It would be better to use double quotes so that yo
scheglov
2014/05/25 16:16:38
Done.
|
| + } |
| + _MemoryFolder folder = null; |
| + String partialPath = ""; |
|
Brian Wilkerson
2014/05/25 15:29:58
It's probably more efficient to use a StringBuffer
scheglov
2014/05/25 16:16:38
It is surprising, but no, it is not faster.
Actual
|
| + for (String pathPart in path.split('/')) { |
| + if (pathPart.isEmpty) { |
| + continue; |
| + } |
| + partialPath += '/' + pathPart; |
| + _MemoryResource resource = _pathToResource[partialPath]; |
| + if (resource == null) { |
| + folder = new _MemoryFolder(this, partialPath); |
| + _pathToResource[partialPath] = folder; |
| + _pathToTimestamp[partialPath] = nextStamp++; |
| + } else if (resource is _MemoryFolder) { |
| + folder = resource; |
| + } else { |
| + String message = 'Folder expected at '; |
| + message += "'$partialPath'"; |
| + message += 'but ${resource.runtimeType} found'; |
|
Brian Wilkerson
2014/05/25 15:29:58
It looks really strange to have a mixture of "+" a
scheglov
2014/05/25 16:16:38
Mostly because it does not fit one 80 characters l
|
| + throw new ArgumentError(message); |
| + } |
| + } |
| + return folder; |
| + } |
| + |
| + File newFile(String path, String content) { |
| + path = normalize(path); |
| + newFolder(dirname(path)); |
| + _MemoryFile file = new _MemoryFile(this, path); |
| + _pathToResource[path] = file; |
| + _pathToContent[path] = content; |
| + _pathToTimestamp[path] = nextStamp++; |
| + return file; |
| + } |
| +} |
| + |
| + |
| +/** |
| + * A `dart:io` based implementation of [File]. |
| + */ |
| +class _PhysicalFile extends _PhysicalResource implements File { |
| + _PhysicalFile(io.File file) : super(file); |
| + |
| + @override |
| + Source createSource(UriKind uriKind) { |
| + io.File file = _entry as io.File; |
| + JavaFile javaFile = new JavaFile(file.absolute.path); |
| + return new FileBasedSource.con2(javaFile, uriKind); |
| + } |
| +} |
| + |
| + |
| +/** |
| + * A `dart:io` based implementation of [Folder]. |
| + */ |
| +class _PhysicalFolder extends _PhysicalResource implements Folder { |
| + _PhysicalFolder(io.Directory directory) : super(directory); |
| + |
| + @override |
| + Resource getChild(String relPath) { |
| + String childPath = join(_entry.absolute.path, relPath); |
| + return PhysicalResourceProvider.INSTANCE.getResource(childPath); |
| + } |
| + |
| + @override |
| + List<Resource> getChildren() { |
| + List<Resource> children = []; |
| + io.Directory directory = _entry as io.Directory; |
| + List<io.FileSystemEntity> entries = directory.listSync(recursive: false); |
| + int numEntries = entries.length; |
| + for (int i = 0; i < numEntries; i++) { |
| + io.FileSystemEntity entity = entries[i]; |
| + if (entity is io.Directory) { |
| + children.add(new _PhysicalFolder(entity)); |
| + } else if (entity is io.File) { |
| + children.add(new _PhysicalFile(entity)); |
| + } |
| + } |
| + return children; |
| + } |
| +} |
| + |
| + |
| +/** |
| + * A `dart:io` based implementation of [Resource]. |
| + */ |
| +abstract class _PhysicalResource implements Resource { |
| + final io.FileSystemEntity _entry; |
| + |
| + _PhysicalResource(this._entry); |
| + |
| + @override |
| + bool get exists => _entry.existsSync(); |
| + |
| + @override |
| + String get fullName => _entry.absolute.path; |
| + |
| + @override |
| + get hashCode => _entry.hashCode; |
| + |
| + @override |
| + String get shortName => basename(fullName); |
| + |
| + @override |
| + String toString() => fullName; |
| +} |
| + |
| + |
| +/** |
| + * A `dart:io` based implementation of [ResourceProvider]. |
| + */ |
| +class PhysicalResourceProvider implements ResourceProvider { |
| + static final PhysicalResourceProvider INSTANCE = new PhysicalResourceProvider._(); |
| + |
| + PhysicalResourceProvider._(); |
| + |
| + @override |
| + Resource getResource(String path) { |
| + if (io.FileSystemEntity.isDirectorySync(path)) { |
| + io.Directory directory = new io.Directory(path); |
| + return new _PhysicalFolder(directory); |
| + } else { |
| + io.File file = new io.File(path); |
| + return new _PhysicalFile(file); |
| + } |
| + } |
| +} |