4366ff1afd
Change-Id: Ic4266f4ad1f46e405e22986e3660a3889ec7016c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/187060 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
715 lines
20 KiB
Dart
715 lines
20 KiB
Dart
// 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.
|
|
|
|
import 'dart:async';
|
|
import 'dart:collection';
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/src/generated/source_io.dart';
|
|
import 'package:analyzer/src/source/source_resource.dart';
|
|
import 'package:path/path.dart' as pathos;
|
|
import 'package:watcher/watcher.dart';
|
|
|
|
/// An in-memory implementation of [ResourceProvider].
|
|
/// Use `/` as a path separator.
|
|
class MemoryResourceProvider implements ResourceProvider {
|
|
final Map<String, _MemoryResource> _pathToResource =
|
|
HashMap<String, _MemoryResource>();
|
|
final Map<String, Uint8List> _pathToBytes = HashMap<String, Uint8List>();
|
|
final Map<String, int> _pathToTimestamp = HashMap<String, int>();
|
|
final Map<String, String> _pathToLinkedPath = {};
|
|
final Map<String, List<StreamController<WatchEvent>>> _pathToWatchers =
|
|
HashMap<String, List<StreamController<WatchEvent>>>();
|
|
int nextStamp = 0;
|
|
|
|
final pathos.Context _pathContext;
|
|
|
|
MemoryResourceProvider(
|
|
{pathos.Context? context, @deprecated bool isWindows = false})
|
|
: _pathContext = context ??= pathos.style == pathos.Style.windows
|
|
// On Windows, ensure that the current drive matches
|
|
// the drive inserted by MemoryResourceProvider.convertPath
|
|
// so that packages are mapped to the correct drive
|
|
? pathos.Context(current: 'C:\\')
|
|
: pathos.context;
|
|
|
|
@override
|
|
pathos.Context get pathContext => _pathContext;
|
|
|
|
/// Convert the given posix [path] to conform to this provider's path context.
|
|
///
|
|
/// This is a utility method for testing; paths passed in to other methods in
|
|
/// this class are never converted automatically.
|
|
String convertPath(String path) {
|
|
if (pathContext.style == pathos.windows.style) {
|
|
if (path.startsWith(pathos.posix.separator)) {
|
|
path = r'C:' + path;
|
|
}
|
|
path = path.replaceAll(pathos.posix.separator, pathos.windows.separator);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/// Delete the file with the given path.
|
|
void deleteFile(String path) {
|
|
_checkFileAtPath(path);
|
|
_pathToResource.remove(path);
|
|
_pathToBytes.remove(path);
|
|
_pathToTimestamp.remove(path);
|
|
_notifyWatchers(path, ChangeType.REMOVE);
|
|
}
|
|
|
|
/// Delete the folder with the given path
|
|
/// and recursively delete nested files and folders.
|
|
void deleteFolder(String path) {
|
|
_checkFolderAtPath(path);
|
|
var folder = _pathToResource[path] as _MemoryFolder;
|
|
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);
|
|
_pathToBytes.remove(path);
|
|
_pathToTimestamp.remove(path);
|
|
_notifyWatchers(path, ChangeType.REMOVE);
|
|
}
|
|
|
|
@override
|
|
File getFile(String path) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
return _MemoryFile(this, path);
|
|
}
|
|
|
|
@override
|
|
Folder getFolder(String path) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
return _MemoryFolder(this, path);
|
|
}
|
|
|
|
@override
|
|
Future<List<int>> getModificationTimes(List<Source> sources) async {
|
|
return sources.map((source) {
|
|
String path = source.fullName;
|
|
return _pathToTimestamp[path] ?? -1;
|
|
}).toList();
|
|
}
|
|
|
|
@override
|
|
Resource getResource(String path) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
return _pathToResource[path] ?? _MemoryFile(this, path);
|
|
}
|
|
|
|
@override
|
|
Folder getStateLocation(String pluginId) {
|
|
var path = convertPath('/user/home/$pluginId');
|
|
return newFolder(path);
|
|
}
|
|
|
|
void modifyFile(String path, String content) {
|
|
_checkFileAtPath(path);
|
|
_pathToBytes[path] = utf8.encode(content) as Uint8List;
|
|
_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) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
newFolder(pathContext.dirname(path));
|
|
_MemoryDummyLink link = _MemoryDummyLink(this, path);
|
|
_pathToResource[path] = link;
|
|
_pathToTimestamp[path] = nextStamp++;
|
|
_notifyWatchers(path, ChangeType.ADD);
|
|
return link;
|
|
}
|
|
|
|
File newFile(String path, String content, [int? stamp]) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
_MemoryFile file = _newFile(path);
|
|
_pathToBytes[path] = utf8.encode(content) as Uint8List;
|
|
_pathToTimestamp[path] = stamp ?? nextStamp++;
|
|
_notifyWatchers(path, ChangeType.ADD);
|
|
return file;
|
|
}
|
|
|
|
File newFileWithBytes(String path, List<int> bytes, [int? stamp]) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
_MemoryFile file = _newFile(path);
|
|
_pathToBytes[path] = Uint8List.fromList(bytes);
|
|
_pathToTimestamp[path] = stamp ?? nextStamp++;
|
|
_notifyWatchers(path, ChangeType.ADD);
|
|
return file;
|
|
}
|
|
|
|
Folder newFolder(String path) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
if (!pathContext.isAbsolute(path)) {
|
|
throw ArgumentError("Path must be absolute : $path");
|
|
}
|
|
var resource = _pathToResource[path];
|
|
if (resource == null) {
|
|
String parentPath = pathContext.dirname(path);
|
|
if (parentPath != path) {
|
|
newFolder(parentPath);
|
|
}
|
|
_MemoryFolder folder = _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 ArgumentError(message);
|
|
}
|
|
}
|
|
|
|
/// Create a link from the [path] to the [target].
|
|
void newLink(String path, String target) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
_ensureAbsoluteAndNormalized(target);
|
|
_pathToLinkedPath[path] = target;
|
|
}
|
|
|
|
File updateFile(String path, String content, [int? stamp]) {
|
|
_ensureAbsoluteAndNormalized(path);
|
|
newFolder(pathContext.dirname(path));
|
|
_MemoryFile file = _MemoryFile(this, path);
|
|
_pathToResource[path] = file;
|
|
_pathToBytes[path] = utf8.encode(content) as Uint8List;
|
|
_pathToTimestamp[path] = stamp ?? nextStamp++;
|
|
_notifyWatchers(path, ChangeType.MODIFY);
|
|
return file;
|
|
}
|
|
|
|
/// Write a representation of the file system on the given [sink].
|
|
void writeOn(StringSink sink) {
|
|
List<String> paths = _pathToResource.keys.toList();
|
|
paths.sort();
|
|
paths.forEach(sink.writeln);
|
|
}
|
|
|
|
void _checkFileAtPath(String path) {
|
|
// TODO(brianwilkerson) Consider throwing a FileSystemException rather than
|
|
// an ArgumentError.
|
|
var resource = _pathToResource[path];
|
|
if (resource is! _MemoryFile) {
|
|
if (resource == null) {
|
|
throw ArgumentError('File expected at "$path" but does not exist');
|
|
}
|
|
throw ArgumentError(
|
|
'File expected at "$path" but ${resource.runtimeType} found');
|
|
}
|
|
}
|
|
|
|
void _checkFolderAtPath(String path) {
|
|
// TODO(brianwilkerson) Consider throwing a FileSystemException rather than
|
|
// an ArgumentError.
|
|
var resource = _pathToResource[path];
|
|
if (resource is! _MemoryFolder) {
|
|
throw ArgumentError(
|
|
'Folder expected at "$path" but ${resource.runtimeType} found');
|
|
}
|
|
}
|
|
|
|
/// The file system abstraction supports only absolute and normalized paths.
|
|
/// This method is used to validate any input paths to prevent errors later.
|
|
void _ensureAbsoluteAndNormalized(String path) {
|
|
if (!pathContext.isAbsolute(path)) {
|
|
throw ArgumentError("Path must be absolute : $path");
|
|
}
|
|
if (pathContext.normalize(path) != path) {
|
|
throw ArgumentError("Path must be normalized : $path");
|
|
}
|
|
}
|
|
|
|
/// Create a new [_MemoryFile] without any content.
|
|
_MemoryFile _newFile(String path) {
|
|
String folderPath = pathContext.dirname(path);
|
|
var folder = _pathToResource[folderPath];
|
|
if (folder == null) {
|
|
newFolder(folderPath);
|
|
} else if (folder is! Folder) {
|
|
throw ArgumentError('Cannot create file ($path) as child of file');
|
|
}
|
|
_MemoryFile file = _MemoryFile(this, path);
|
|
_pathToResource[path] = file;
|
|
return file;
|
|
}
|
|
|
|
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(WatchEvent(changeType, path));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
_MemoryFile _renameFileSync(_MemoryFile file, String newPath) {
|
|
String path = file.path;
|
|
if (newPath == path) {
|
|
return file;
|
|
}
|
|
var existingNewResource = _pathToResource[newPath];
|
|
if (existingNewResource is _MemoryFolder) {
|
|
throw FileSystemException(
|
|
path, 'Could not be renamed: $newPath is a folder.');
|
|
}
|
|
|
|
_MemoryFile newFile = _newFile(newPath);
|
|
_pathToResource.remove(path);
|
|
|
|
var oldBytes = _pathToBytes.remove(path);
|
|
if (oldBytes != null) {
|
|
_pathToBytes[newPath] = oldBytes;
|
|
}
|
|
|
|
var oldTimestamp = _pathToTimestamp.remove(path);
|
|
if (oldTimestamp != null) {
|
|
_pathToTimestamp[newPath] = oldTimestamp;
|
|
}
|
|
|
|
if (existingNewResource != null) {
|
|
_notifyWatchers(newPath, ChangeType.REMOVE);
|
|
}
|
|
_notifyWatchers(path, ChangeType.REMOVE);
|
|
_notifyWatchers(newPath, ChangeType.ADD);
|
|
return newFile;
|
|
}
|
|
|
|
String _resolveLinks(String path) {
|
|
var parentPath = _pathContext.dirname(path);
|
|
if (parentPath == path) {
|
|
return path;
|
|
}
|
|
|
|
var canonicalParentPath = _resolveLinks(parentPath);
|
|
|
|
var baseName = _pathContext.basename(path);
|
|
var result = _pathContext.join(canonicalParentPath, baseName);
|
|
|
|
do {
|
|
var linkTarget = _pathToLinkedPath[result];
|
|
if (linkTarget != null) {
|
|
result = linkTarget;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (true);
|
|
|
|
return result;
|
|
}
|
|
|
|
void _setFileContent(_MemoryFile file, List<int> bytes) {
|
|
String path = file.path;
|
|
_pathToResource[path] = file;
|
|
_pathToBytes[path] = Uint8List.fromList(bytes);
|
|
_pathToTimestamp[path] = nextStamp++;
|
|
_notifyWatchers(path, ChangeType.MODIFY);
|
|
}
|
|
}
|
|
|
|
/// 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 FileSystemException(path, "File does not exist");
|
|
}
|
|
|
|
@override
|
|
bool get exists => false;
|
|
|
|
@override
|
|
int get lengthSync {
|
|
throw FileSystemException(path, 'File could not be read');
|
|
}
|
|
|
|
@override
|
|
int get modificationStamp {
|
|
var stamp = provider._pathToTimestamp[path];
|
|
if (stamp == null) {
|
|
throw FileSystemException(path, "File does not exist");
|
|
}
|
|
return stamp;
|
|
}
|
|
|
|
@override
|
|
File copyTo(Folder parentFolder) {
|
|
throw FileSystemException(path, 'File could not be copied');
|
|
}
|
|
|
|
@override
|
|
Source createSource([Uri? uri]) {
|
|
throw FileSystemException(path, 'File could not be read');
|
|
}
|
|
|
|
@override
|
|
void delete() {
|
|
throw FileSystemException(path, 'File could not be deleted');
|
|
}
|
|
|
|
@override
|
|
bool isOrContains(String path) {
|
|
return path == this.path;
|
|
}
|
|
|
|
@override
|
|
Uint8List readAsBytesSync() {
|
|
throw FileSystemException(path, 'File could not be read');
|
|
}
|
|
|
|
@override
|
|
String readAsStringSync() {
|
|
throw FileSystemException(path, 'File could not be read');
|
|
}
|
|
|
|
@override
|
|
File renameSync(String newPath) {
|
|
throw FileSystemException(path, 'File could not be renamed');
|
|
}
|
|
|
|
@override
|
|
File resolveSymbolicLinksSync() {
|
|
return throw FileSystemException(path, "File does not exist");
|
|
}
|
|
|
|
@override
|
|
void writeAsBytesSync(List<int> bytes) {
|
|
throw FileSystemException(path, 'File could not be written');
|
|
}
|
|
|
|
@override
|
|
void writeAsStringSync(String content) {
|
|
throw FileSystemException(path, 'File could not be written');
|
|
}
|
|
}
|
|
|
|
/// An in-memory implementation of [File].
|
|
class _MemoryFile extends _MemoryResource implements File {
|
|
_MemoryFile(MemoryResourceProvider provider, String path)
|
|
: super(provider, path);
|
|
|
|
@override
|
|
bool get exists {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
return provider._pathToResource[canonicalPath] is _MemoryFile;
|
|
}
|
|
|
|
@override
|
|
int get lengthSync {
|
|
return readAsBytesSync().length;
|
|
}
|
|
|
|
@override
|
|
int get modificationStamp {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
var stamp = provider._pathToTimestamp[canonicalPath];
|
|
if (stamp == null) {
|
|
throw FileSystemException(path, 'File "$path" does not exist.');
|
|
}
|
|
return stamp;
|
|
}
|
|
|
|
@override
|
|
File copyTo(Folder parentFolder) {
|
|
parentFolder.create();
|
|
File destination = parentFolder.getChildAssumingFile(shortName);
|
|
destination.writeAsBytesSync(readAsBytesSync());
|
|
return destination;
|
|
}
|
|
|
|
@override
|
|
Source createSource([Uri? uri]) {
|
|
uri ??= provider.pathContext.toUri(path);
|
|
return FileSource(this, uri);
|
|
}
|
|
|
|
@override
|
|
void delete() {
|
|
provider.deleteFile(path);
|
|
}
|
|
|
|
@override
|
|
bool isOrContains(String path) {
|
|
return path == this.path;
|
|
}
|
|
|
|
@override
|
|
Uint8List readAsBytesSync() {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
var content = provider._pathToBytes[canonicalPath];
|
|
if (content == null) {
|
|
throw FileSystemException(path, 'File "$path" does not exist.');
|
|
}
|
|
return content;
|
|
}
|
|
|
|
@override
|
|
String readAsStringSync() {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
var content = provider._pathToBytes[canonicalPath];
|
|
if (content == null) {
|
|
throw FileSystemException(path, 'File "$path" does not exist.');
|
|
}
|
|
return utf8.decode(content);
|
|
}
|
|
|
|
@override
|
|
File renameSync(String newPath) {
|
|
return provider._renameFileSync(this, newPath);
|
|
}
|
|
|
|
@override
|
|
File resolveSymbolicLinksSync() {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
var result = provider.getFile(canonicalPath);
|
|
|
|
if (!result.exists) {
|
|
throw FileSystemException(path, 'File "$path" does not exist.');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
void writeAsBytesSync(List<int> bytes) {
|
|
provider._setFileContent(this, bytes);
|
|
}
|
|
|
|
@override
|
|
void writeAsStringSync(String content) {
|
|
provider._setFileContent(this, utf8.encode(content));
|
|
}
|
|
}
|
|
|
|
/// An in-memory implementation of [Folder].
|
|
class _MemoryFolder extends _MemoryResource implements Folder {
|
|
_MemoryFolder(MemoryResourceProvider provider, String path)
|
|
: super(provider, path);
|
|
|
|
@override
|
|
bool get exists {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
return provider._pathToResource[canonicalPath] is _MemoryFolder;
|
|
}
|
|
|
|
@override
|
|
bool get isRoot {
|
|
var parentPath = provider.pathContext.dirname(path);
|
|
return parentPath == path;
|
|
}
|
|
|
|
@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
|
|
Folder copyTo(Folder parentFolder) {
|
|
Folder destination = parentFolder.getChildAssumingFolder(shortName);
|
|
destination.create();
|
|
for (Resource child in getChildren()) {
|
|
child.copyTo(destination);
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
@override
|
|
void create() {
|
|
provider.newFolder(path);
|
|
}
|
|
|
|
@override
|
|
void delete() {
|
|
provider.deleteFolder(path);
|
|
}
|
|
|
|
@override
|
|
Resource getChild(String relPath) {
|
|
String childPath = canonicalizePath(relPath);
|
|
return provider._pathToResource[childPath] ??
|
|
_MemoryFile(provider, childPath);
|
|
}
|
|
|
|
@override
|
|
_MemoryFile getChildAssumingFile(String relPath) {
|
|
String childPath = canonicalizePath(relPath);
|
|
var resource = provider._pathToResource[childPath];
|
|
if (resource is _MemoryFile) {
|
|
return resource;
|
|
}
|
|
return _MemoryFile(provider, childPath);
|
|
}
|
|
|
|
@override
|
|
_MemoryFolder getChildAssumingFolder(String relPath) {
|
|
String childPath = canonicalizePath(relPath);
|
|
var resource = provider._pathToResource[childPath];
|
|
if (resource is _MemoryFolder) {
|
|
return resource;
|
|
}
|
|
return _MemoryFolder(provider, childPath);
|
|
}
|
|
|
|
@override
|
|
List<Resource> getChildren() {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
if (canonicalPath != path) {
|
|
var target = provider.getFolder(canonicalPath);
|
|
var canonicalChildren = target.getChildren();
|
|
return canonicalChildren.map((child) {
|
|
var childPath = provider.pathContext.join(path, child.shortName);
|
|
if (child is Folder) {
|
|
return _MemoryFolder(provider, childPath);
|
|
} else {
|
|
return _MemoryFile(provider, childPath);
|
|
}
|
|
}).toList();
|
|
}
|
|
|
|
if (!exists) {
|
|
throw FileSystemException(path, 'Folder does not exist.');
|
|
}
|
|
|
|
var children = <Resource>[];
|
|
|
|
provider._pathToResource.forEach((resourcePath, resource) {
|
|
if (provider.pathContext.dirname(resourcePath) == path) {
|
|
children.add(resource);
|
|
}
|
|
});
|
|
|
|
provider._pathToLinkedPath.forEach((resourcePath, targetPath) {
|
|
if (provider.pathContext.dirname(resourcePath) == path) {
|
|
var target = provider.getResource(targetPath);
|
|
if (target is File) {
|
|
children.add(
|
|
_MemoryFile(provider, resourcePath),
|
|
);
|
|
} else if (target is Folder) {
|
|
children.add(
|
|
_MemoryFolder(provider, resourcePath),
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
return children;
|
|
}
|
|
|
|
@override
|
|
bool isOrContains(String path) {
|
|
if (path == this.path) {
|
|
return true;
|
|
}
|
|
return contains(path);
|
|
}
|
|
|
|
@override
|
|
Folder resolveSymbolicLinksSync() {
|
|
var canonicalPath = provider._resolveLinks(path);
|
|
var result = provider.getFolder(canonicalPath);
|
|
|
|
if (!result.exists) {
|
|
throw FileSystemException(path, 'Folder "$path" does not exist.');
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
Uri toUri() => provider.pathContext.toUri(path + '/');
|
|
}
|
|
|
|
/// An in-memory implementation of [Resource].
|
|
abstract class _MemoryResource implements Resource {
|
|
@override
|
|
final MemoryResourceProvider provider;
|
|
|
|
@override
|
|
final String path;
|
|
|
|
_MemoryResource(this.provider, this.path);
|
|
|
|
Stream<WatchEvent> get changes {
|
|
StreamController<WatchEvent> streamController =
|
|
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
|
|
int get hashCode => path.hashCode;
|
|
|
|
@Deprecated('Use parent2 instead')
|
|
@override
|
|
Folder? get parent {
|
|
String parentPath = provider.pathContext.dirname(path);
|
|
if (parentPath == path) {
|
|
return null;
|
|
}
|
|
return provider.getFolder(parentPath);
|
|
}
|
|
|
|
@override
|
|
Folder get parent2 {
|
|
String parentPath = provider.pathContext.dirname(path);
|
|
return provider.getFolder(parentPath);
|
|
}
|
|
|
|
@override
|
|
String get shortName => provider.pathContext.basename(path);
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (runtimeType != other.runtimeType) {
|
|
return false;
|
|
}
|
|
return path == (other as _MemoryResource).path;
|
|
}
|
|
|
|
@override
|
|
String toString() => path;
|
|
|
|
@override
|
|
Uri toUri() => provider.pathContext.toUri(path);
|
|
}
|