Index: ui/file_manager/file_manager/background/js/file_operation_manager.js |
diff --git a/ui/file_manager/file_manager/background/js/file_operation_manager.js b/ui/file_manager/file_manager/background/js/file_operation_manager.js |
index 54226a3bae5b13f062d57ae2a8dbe96bee66c544..82e20e5471cadc62f2fe3102d8d6a37e8debf817 100644 |
--- a/ui/file_manager/file_manager/background/js/file_operation_manager.js |
+++ b/ui/file_manager/file_manager/background/js/file_operation_manager.js |
@@ -10,40 +10,102 @@ |
var fileOperationUtil = {}; |
/** |
- * Simple wrapper for util.deduplicatePath. On error, this method translates |
- * the FileError to FileOperationManager.Error object. |
+ * Resolves a path to either a DirectoryEntry or a FileEntry, regardless of |
+ * whether the path is a directory or file. |
+ * |
+ * @param {DirectoryEntry} root The root of the filesystem to search. |
+ * @param {string} path The path to be resolved. |
+ * @return {Promise} Promise fulfilled with the resolved entry, or rejected with |
+ * FileError. |
+ */ |
+fileOperationUtil.resolvePath = function(root, path) { |
+ if (path === '' || path === '/') |
+ return Promise.resolve(root); |
+ return new Promise(root.getFile.bind(root, path, {create: false})). |
+ catch(function(error) { |
+ if (error.name === util.FileError.TYPE_MISMATCH_ERR) { |
+ // Bah. It's a directory, ask again. |
+ return new Promise( |
+ root.getDirectory.bind(root, path, {create: false})); |
+ } else { |
+ return Promise.reject(error); |
+ } |
+ }); |
+}; |
+ |
+/** |
+ * Checks if an entry exists at |relativePath| in |dirEntry|. |
+ * If exists, tries to deduplicate the path by inserting parenthesized number, |
+ * such as " (1)", before the extension. If it still exists, tries the |
+ * deduplication again by increasing the number up to 10 times. |
+ * For example, suppose "file.txt" is given, "file.txt", "file (1).txt", |
+ * "file (2).txt", ..., "file (9).txt" will be tried. |
* |
* @param {DirectoryEntry} dirEntry The target directory entry. |
* @param {string} relativePath The path to be deduplicated. |
- * @param {function(string)} successCallback Callback run with the deduplicated |
- * path on success. |
- * @param {function(FileOperationManager.Error)} errorCallback Callback run on |
- * error. |
+ * @param {function(string)=} opt_successCallback Callback run with the |
+ * deduplicated path on success. |
+ * @param {function(FileOperationManager.Error)=} opt_errorCallback Callback run |
+ * on error. |
*/ |
fileOperationUtil.deduplicatePath = function( |
- dirEntry, relativePath, successCallback, errorCallback) { |
- util.deduplicatePath( |
- dirEntry, relativePath, successCallback, |
- function(err) { |
- var onFileSystemError = function(error) { |
- errorCallback(new FileOperationManager.Error( |
- util.FileOperationErrorType.FILESYSTEM_ERROR, error)); |
- }; |
- |
- if (err.name == util.FileError.PATH_EXISTS_ERR) { |
- // Failed to uniquify the file path. There should be an existing |
- // entry, so return the error with it. |
- util.resolvePath( |
- dirEntry, relativePath, |
- function(entry) { |
- errorCallback(new FileOperationManager.Error( |
- util.FileOperationErrorType.TARGET_EXISTS, entry)); |
- }, |
- onFileSystemError); |
- return; |
- } |
- onFileSystemError(err); |
- }); |
+ dirEntry, relativePath, opt_successCallback, opt_errorCallback) { |
+ // The trial is up to 10. |
+ var MAX_RETRY = 10; |
+ |
+ // Crack the path into three part. The parenthesized number (if exists) will |
+ // be replaced by incremented number for retry. For example, suppose |
+ // |relativePath| is "file (10).txt", the second check path will be |
+ // "file (11).txt". |
+ var match = /^(.*?)(?: \((\d+)\))?(\.[^.]*?)?$/.exec(relativePath); |
+ var prefix = match[1]; |
+ var ext = match[3] || ''; |
+ |
+ // Check to see if the target exists. |
+ var resolvePath = function(trialPath, numRetry, copyNumber) { |
+ return fileOperationUtil.resolvePath(dirEntry, trialPath).then(function() { |
+ if (numRetry <= 1) { |
+ // Hit the limit of the number of retrial. |
+ // Note that we cannot create FileError object directly, so here we |
+ // use Object.create instead. |
+ return Promise.reject( |
+ util.createDOMError(util.FileError.PATH_EXISTS_ERR)); |
+ } |
+ var newTrialPath = prefix + ' (' + copyNumber + ')' + ext; |
+ return resolvePath(newTrialPath, numRetry - 1, copyNumber + 1); |
+ }, function(error) { |
+ // We expect to be unable to resolve the target file, since we're |
+ // going to create it during the copy. However, if the resolve fails |
+ // with anything other than NOT_FOUND, that's trouble. |
+ if (error.name === util.FileError.NOT_FOUND_ERR) |
+ return trialPath; |
+ else |
+ return Promise.reject(error); |
+ }); |
+ }; |
+ |
+ var promise = resolvePath(relativePath, MAX_RETRY, 1).catch(function(error) { |
+ var targetPromise; |
+ if (error.name === util.FileError.PATH_EXISTS_ERR) { |
+ // Failed to uniquify the file path. There should be an existing |
+ // entry, so return the error with it. |
+ targetPromise = fileOperationUtil.resolvePath(dirEntry, relativePath); |
+ } else { |
+ targetPromise = Promise.reject(error); |
+ } |
+ return targetPromise.then(function(entry) { |
+ return Promise.reject(new FileOperationManager.Error( |
+ util.FileOperationErrorType.TARGET_EXISTS, entry)); |
+ }, function(inError) { |
+ if (inError instanceof Error) |
+ return Promise.reject(inError); |
+ return Promise.reject(new FileOperationManager.Error( |
+ util.FileOperationErrorType.FILESYSTEM_ERROR, inError)); |
+ }); |
+ }); |
+ if (opt_successCallback) |
+ promise.then(opt_successCallback, opt_errorCallback); |
+ return promise; |
}; |
/** |
@@ -530,13 +592,13 @@ FileOperationManager.CopyTask = function(sourceEntries, |
targetDirEntry); |
this.deleteAfterCopy = deleteAfterCopy; |
- /* |
+ /** |
* Rate limiter which is used to avoid sending update request for progress bar |
* too frequently. |
* @type {AsyncUtil.RateLimiter} |
* @private |
*/ |
- this.updateProgressRateLimiter_ = null |
+ this.updateProgressRateLimiter_ = null; |
}; |
/** |