mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 20:13:39 +00:00
5b95688b5f
BUG= R=ajohnsen@google.com Review URL: https://codereview.chromium.org//25173004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28056 260f80e4-7a28-3924-810f-c04153c831b5
378 lines
11 KiB
Dart
378 lines
11 KiB
Dart
// Copyright (c) 2012, 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.
|
|
|
|
part of dart.io;
|
|
|
|
class _Directory extends FileSystemEntity implements Directory {
|
|
final String path;
|
|
|
|
_Directory(String this.path) {
|
|
if (path is! String) {
|
|
throw new ArgumentError('${Error.safeToString(path)} '
|
|
'is not a String');
|
|
}
|
|
}
|
|
|
|
external static _current();
|
|
external static _setCurrent(path);
|
|
external static _createTemp(String template);
|
|
external static int _exists(String path);
|
|
external static _create(String path);
|
|
external static _deleteNative(String path, bool recursive);
|
|
external static _rename(String path, String newPath);
|
|
external static List _list(String path, bool recursive, bool followLinks);
|
|
|
|
static Directory get current {
|
|
var result = _current();
|
|
if (result is OSError) {
|
|
throw new DirectoryException(
|
|
"Getting current working directory failed", "", result);
|
|
}
|
|
return new _Directory(result);
|
|
}
|
|
|
|
static void set current(path) {
|
|
if (path is Directory) path = path.path;
|
|
var result = _setCurrent(path);
|
|
if (result is ArgumentError) throw result;
|
|
if (result is OSError) {
|
|
throw new DirectoryException(
|
|
"Setting current working directory failed", path, result);
|
|
}
|
|
}
|
|
|
|
Future<bool> exists() {
|
|
return _IOService.dispatch(_DIRECTORY_EXISTS, [path]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionOrErrorFromResponse(response, "Exists failed");
|
|
}
|
|
return response == 1;
|
|
});
|
|
}
|
|
|
|
bool existsSync() {
|
|
var result = _exists(path);
|
|
if (result is OSError) {
|
|
throw new DirectoryException("Exists failed", path, result);
|
|
}
|
|
return (result == 1);
|
|
}
|
|
|
|
Directory get absolute => new Directory(_absolutePath);
|
|
|
|
Future<FileStat> stat() => FileStat.stat(path);
|
|
|
|
FileStat statSync() => FileStat.statSync(path);
|
|
|
|
// Compute the index of the first directory in the list that exists. If
|
|
// none of the directories exist dirsToCreate.length is returned.
|
|
Future<int> _computeExistingIndex(List dirsToCreate) {
|
|
var future;
|
|
var notFound = dirsToCreate.length;
|
|
for (var i = 0; i < dirsToCreate.length; i++) {
|
|
if (future == null) {
|
|
future = dirsToCreate[i].exists().then((e) => e ? i : notFound);
|
|
} else {
|
|
future = future.then((index) {
|
|
if (index != notFound) {
|
|
return new Future.value(index);
|
|
}
|
|
return dirsToCreate[i].exists().then((e) => e ? i : notFound);
|
|
});
|
|
}
|
|
}
|
|
if (future == null) {
|
|
return new Future.value(notFound);
|
|
} else {
|
|
return future;
|
|
}
|
|
}
|
|
|
|
Future<Directory> createRecursively() {
|
|
var path = new _Path(this.path);
|
|
var dirsToCreate = [];
|
|
var terminator = path.isAbsolute ? '/' : '';
|
|
while (path.toString() != terminator) {
|
|
dirsToCreate.add(new Directory(path.toNativePath()));
|
|
path = path.directoryPath;
|
|
}
|
|
return _computeExistingIndex(dirsToCreate).then((index) {
|
|
var future;
|
|
for (var i = index - 1; i >= 0 ; i--) {
|
|
if (future == null) {
|
|
future = dirsToCreate[i].create();
|
|
} else {
|
|
future = future.then((_) {
|
|
return dirsToCreate[i].create();
|
|
});
|
|
}
|
|
}
|
|
if (future == null) {
|
|
return new Future.value(this);
|
|
} else {
|
|
return future.then((_) => this);
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<Directory> create({bool recursive: false}) {
|
|
if (recursive) return createRecursively();
|
|
return _IOService.dispatch(_DIRECTORY_CREATE, [path]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionOrErrorFromResponse(response, "Creation failed");
|
|
}
|
|
return this;
|
|
});
|
|
}
|
|
|
|
void createRecursivelySync() {
|
|
var path = new _Path(this.path);
|
|
var dirsToCreate = [];
|
|
var terminator = path.isAbsolute ? '/' : '';
|
|
while (path.toString() != terminator) {
|
|
var dir = new Directory(path.toNativePath());
|
|
if (dir.existsSync()) break;
|
|
dirsToCreate.add(dir);
|
|
path = path.directoryPath;
|
|
}
|
|
for (var i = dirsToCreate.length - 1; i >= 0; i--) {
|
|
dirsToCreate[i].createSync();
|
|
}
|
|
}
|
|
|
|
void createSync({bool recursive: false}) {
|
|
if (recursive) return createRecursivelySync();
|
|
var result = _create(path);
|
|
if (result is OSError) {
|
|
throw new DirectoryException("Creation failed", path, result);
|
|
}
|
|
}
|
|
|
|
Future<Directory> createTemp() {
|
|
return _IOService.dispatch(_DIRECTORY_CREATE_TEMP, [path]).then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionOrErrorFromResponse(
|
|
response, "Creation of temporary directory failed");
|
|
}
|
|
return new Directory(response);
|
|
});
|
|
}
|
|
|
|
Directory createTempSync() {
|
|
var result = _createTemp(path);
|
|
if (result is OSError) {
|
|
throw new DirectoryException("Creation of temporary directory failed",
|
|
path,
|
|
result);
|
|
}
|
|
return new Directory(result);
|
|
}
|
|
|
|
Future<Directory> _delete({bool recursive: false}) {
|
|
return _IOService.dispatch(_DIRECTORY_DELETE, [path, recursive])
|
|
.then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionOrErrorFromResponse(response, "Deletion failed");
|
|
}
|
|
return this;
|
|
});
|
|
}
|
|
|
|
void _deleteSync({bool recursive: false}) {
|
|
var result = _deleteNative(path, recursive);
|
|
if (result is OSError) {
|
|
throw new DirectoryException("Deletion failed", path, result);
|
|
}
|
|
}
|
|
|
|
Future<Directory> rename(String newPath) {
|
|
return _IOService.dispatch(_DIRECTORY_RENAME, [path, newPath])
|
|
.then((response) {
|
|
if (_isErrorResponse(response)) {
|
|
throw _exceptionOrErrorFromResponse(response, "Rename failed");
|
|
}
|
|
return new Directory(newPath);
|
|
});
|
|
}
|
|
|
|
Directory renameSync(String newPath) {
|
|
if (newPath is !String) {
|
|
throw new ArgumentError();
|
|
}
|
|
var result = _rename(path, newPath);
|
|
if (result is OSError) {
|
|
throw new DirectoryException("Rename failed", path, result);
|
|
}
|
|
return new Directory(newPath);
|
|
}
|
|
|
|
Stream<FileSystemEntity> list({bool recursive: false,
|
|
bool followLinks: true}) {
|
|
return new _AsyncDirectoryLister(
|
|
FileSystemEntity._trimTrailingPathSeparators(path),
|
|
recursive,
|
|
followLinks).stream;
|
|
}
|
|
|
|
List listSync({bool recursive: false, bool followLinks: true}) {
|
|
if (recursive is! bool || followLinks is! bool) {
|
|
throw new ArgumentError();
|
|
}
|
|
return _list(
|
|
FileSystemEntity._trimTrailingPathSeparators(path),
|
|
recursive,
|
|
followLinks);
|
|
}
|
|
|
|
String toString() => "Directory: '$path'";
|
|
|
|
bool _isErrorResponse(response) {
|
|
return response is List && response[0] != _SUCCESS_RESPONSE;
|
|
}
|
|
|
|
_exceptionOrErrorFromResponse(response, String message) {
|
|
assert(_isErrorResponse(response));
|
|
switch (response[_ERROR_RESPONSE_ERROR_TYPE]) {
|
|
case _ILLEGAL_ARGUMENT_RESPONSE:
|
|
return new ArgumentError();
|
|
case _OSERROR_RESPONSE:
|
|
var err = new OSError(response[_OSERROR_RESPONSE_MESSAGE],
|
|
response[_OSERROR_RESPONSE_ERROR_CODE]);
|
|
return new DirectoryException(message, path, err);
|
|
default:
|
|
return new Exception("Unknown error");
|
|
}
|
|
}
|
|
}
|
|
|
|
class _AsyncDirectoryLister {
|
|
static const int LIST_FILE = 0;
|
|
static const int LIST_DIRECTORY = 1;
|
|
static const int LIST_LINK = 2;
|
|
static const int LIST_ERROR = 3;
|
|
static const int LIST_DONE = 4;
|
|
|
|
static const int RESPONSE_TYPE = 0;
|
|
static const int RESPONSE_PATH = 1;
|
|
static const int RESPONSE_COMPLETE = 1;
|
|
static const int RESPONSE_ERROR = 2;
|
|
|
|
final String path;
|
|
final bool recursive;
|
|
final bool followLinks;
|
|
|
|
StreamController controller;
|
|
int id;
|
|
bool canceled = false;
|
|
bool nextRunning = false;
|
|
bool closed = false;
|
|
|
|
_AsyncDirectoryLister(String this.path,
|
|
bool this.recursive,
|
|
bool this.followLinks) {
|
|
controller = new StreamController(onListen: onListen,
|
|
onResume: onResume,
|
|
onCancel: onCancel,
|
|
sync: true);
|
|
}
|
|
|
|
Stream get stream => controller.stream;
|
|
|
|
void onListen() {
|
|
_IOService.dispatch(_DIRECTORY_LIST_START, [path, recursive, followLinks])
|
|
.then((response) {
|
|
if (response is int) {
|
|
id = response;
|
|
next();
|
|
} else {
|
|
error(response);
|
|
controller.close();
|
|
}
|
|
});
|
|
}
|
|
|
|
void onResume() {
|
|
if (!nextRunning) next();
|
|
}
|
|
|
|
void onCancel() {
|
|
canceled = true;
|
|
// If we are active, but not requesting, close.
|
|
if (!nextRunning) {
|
|
close();
|
|
}
|
|
}
|
|
|
|
void next() {
|
|
if (canceled) {
|
|
close();
|
|
return;
|
|
}
|
|
if (id == null) return;
|
|
if (controller.isPaused) return;
|
|
assert(!nextRunning);
|
|
nextRunning = true;
|
|
_IOService.dispatch(_DIRECTORY_LIST_NEXT, [id]).then((result) {
|
|
if (result is List) {
|
|
assert(result.length % 2 == 0);
|
|
for (int i = 0; i < result.length; i++) {
|
|
assert(i % 2 == 0);
|
|
switch (result[i++]) {
|
|
case LIST_FILE:
|
|
controller.add(new File(result[i]));
|
|
break;
|
|
case LIST_DIRECTORY:
|
|
controller.add(new Directory(result[i]));
|
|
break;
|
|
case LIST_LINK:
|
|
controller.add(new Link(result[i]));
|
|
break;
|
|
case LIST_ERROR:
|
|
error(result[i]);
|
|
break;
|
|
case LIST_DONE:
|
|
close();
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
controller.addError(new DirectoryException("Internal error"));
|
|
}
|
|
nextRunning = false;
|
|
next();
|
|
});
|
|
}
|
|
|
|
void close() {
|
|
if (closed) return;
|
|
if (id == null) return;
|
|
closed = true;
|
|
_IOService.dispatch(_DIRECTORY_LIST_STOP, [id]).then((_) {
|
|
controller.close();
|
|
});
|
|
}
|
|
|
|
void error(message) {
|
|
var errorType =
|
|
message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE];
|
|
if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) {
|
|
controller.addError(new ArgumentError());
|
|
} else if (errorType == _OSERROR_RESPONSE) {
|
|
var responseError = message[RESPONSE_ERROR];
|
|
var err = new OSError(
|
|
responseError[_OSERROR_RESPONSE_MESSAGE],
|
|
responseError[_OSERROR_RESPONSE_ERROR_CODE]);
|
|
var errorPath = message[RESPONSE_PATH];
|
|
if (errorPath == null) errorPath = path;
|
|
controller.addError(
|
|
new DirectoryException("Directory listing failed",
|
|
errorPath,
|
|
err));
|
|
} else {
|
|
controller.addError(
|
|
new DirectoryException("Internal error"));
|
|
}
|
|
}
|
|
}
|