entryimpl.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. // Copyright 2013 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Concrete implementations of the
  16. * goog.fs.DirectoryEntry, and goog.fs.FileEntry interfaces.
  17. */
  18. goog.provide('goog.fs.DirectoryEntryImpl');
  19. goog.provide('goog.fs.EntryImpl');
  20. goog.provide('goog.fs.FileEntryImpl');
  21. goog.require('goog.array');
  22. goog.require('goog.async.Deferred');
  23. goog.require('goog.fs.DirectoryEntry');
  24. goog.require('goog.fs.Entry');
  25. goog.require('goog.fs.Error');
  26. goog.require('goog.fs.FileEntry');
  27. goog.require('goog.fs.FileWriter');
  28. goog.require('goog.functions');
  29. goog.require('goog.string');
  30. /**
  31. * Base class for concrete implementations of goog.fs.Entry.
  32. * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
  33. * @param {!Entry} entry The underlying Entry object.
  34. * @constructor
  35. * @implements {goog.fs.Entry}
  36. */
  37. goog.fs.EntryImpl = function(fs, entry) {
  38. /**
  39. * The wrapped filesystem.
  40. *
  41. * @type {!goog.fs.FileSystem}
  42. * @private
  43. */
  44. this.fs_ = fs;
  45. /**
  46. * The underlying Entry object.
  47. *
  48. * @type {!Entry}
  49. * @private
  50. */
  51. this.entry_ = entry;
  52. };
  53. /** @override */
  54. goog.fs.EntryImpl.prototype.isFile = function() {
  55. return this.entry_.isFile;
  56. };
  57. /** @override */
  58. goog.fs.EntryImpl.prototype.isDirectory = function() {
  59. return this.entry_.isDirectory;
  60. };
  61. /** @override */
  62. goog.fs.EntryImpl.prototype.getName = function() {
  63. return this.entry_.name;
  64. };
  65. /** @override */
  66. goog.fs.EntryImpl.prototype.getFullPath = function() {
  67. return this.entry_.fullPath;
  68. };
  69. /** @override */
  70. goog.fs.EntryImpl.prototype.getFileSystem = function() {
  71. return this.fs_;
  72. };
  73. /** @override */
  74. goog.fs.EntryImpl.prototype.getLastModified = function() {
  75. return this.getMetadata().addCallback(function(metadata) {
  76. return metadata.modificationTime;
  77. });
  78. };
  79. /** @override */
  80. goog.fs.EntryImpl.prototype.getMetadata = function() {
  81. var d = new goog.async.Deferred();
  82. this.entry_.getMetadata(function(metadata) {
  83. d.callback(metadata);
  84. }, goog.bind(function(err) {
  85. var msg = 'retrieving metadata for ' + this.getFullPath();
  86. d.errback(new goog.fs.Error(err, msg));
  87. }, this));
  88. return d;
  89. };
  90. /** @override */
  91. goog.fs.EntryImpl.prototype.moveTo = function(parent, opt_newName) {
  92. var d = new goog.async.Deferred();
  93. this.entry_.moveTo(
  94. /** @type {!goog.fs.DirectoryEntryImpl} */ (parent).dir_, opt_newName,
  95. goog.bind(function(entry) {
  96. d.callback(this.wrapEntry(entry));
  97. }, this), goog.bind(function(err) {
  98. var msg = 'moving ' + this.getFullPath() + ' into ' +
  99. parent.getFullPath() +
  100. (opt_newName ? ', renaming to ' + opt_newName : '');
  101. d.errback(new goog.fs.Error(err, msg));
  102. }, this));
  103. return d;
  104. };
  105. /** @override */
  106. goog.fs.EntryImpl.prototype.copyTo = function(parent, opt_newName) {
  107. var d = new goog.async.Deferred();
  108. this.entry_.copyTo(
  109. /** @type {!goog.fs.DirectoryEntryImpl} */ (parent).dir_, opt_newName,
  110. goog.bind(function(entry) {
  111. d.callback(this.wrapEntry(entry));
  112. }, this), goog.bind(function(err) {
  113. var msg = 'copying ' + this.getFullPath() + ' into ' +
  114. parent.getFullPath() +
  115. (opt_newName ? ', renaming to ' + opt_newName : '');
  116. d.errback(new goog.fs.Error(err, msg));
  117. }, this));
  118. return d;
  119. };
  120. /** @override */
  121. goog.fs.EntryImpl.prototype.wrapEntry = function(entry) {
  122. return entry.isFile ?
  123. new goog.fs.FileEntryImpl(this.fs_, /** @type {!FileEntry} */ (entry)) :
  124. new goog.fs.DirectoryEntryImpl(
  125. this.fs_, /** @type {!DirectoryEntry} */ (entry));
  126. };
  127. /** @override */
  128. goog.fs.EntryImpl.prototype.toUrl = function(opt_mimeType) {
  129. return this.entry_.toURL(opt_mimeType);
  130. };
  131. /** @override */
  132. goog.fs.EntryImpl.prototype.toUri = goog.fs.EntryImpl.prototype.toUrl;
  133. /** @override */
  134. goog.fs.EntryImpl.prototype.remove = function() {
  135. var d = new goog.async.Deferred();
  136. this.entry_.remove(
  137. goog.bind(d.callback, d, true /* result */), goog.bind(function(err) {
  138. var msg = 'removing ' + this.getFullPath();
  139. d.errback(new goog.fs.Error(err, msg));
  140. }, this));
  141. return d;
  142. };
  143. /** @override */
  144. goog.fs.EntryImpl.prototype.getParent = function() {
  145. var d = new goog.async.Deferred();
  146. this.entry_.getParent(goog.bind(function(parent) {
  147. d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, parent));
  148. }, this), goog.bind(function(err) {
  149. var msg = 'getting parent of ' + this.getFullPath();
  150. d.errback(new goog.fs.Error(err, msg));
  151. }, this));
  152. return d;
  153. };
  154. /**
  155. * A directory in a local FileSystem.
  156. *
  157. * This should not be instantiated directly. Instead, it should be accessed via
  158. * {@link goog.fs.FileSystem#getRoot} or
  159. * {@link goog.fs.DirectoryEntry#getDirectoryEntry}.
  160. *
  161. * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
  162. * @param {!DirectoryEntry} dir The underlying DirectoryEntry object.
  163. * @constructor
  164. * @extends {goog.fs.EntryImpl}
  165. * @implements {goog.fs.DirectoryEntry}
  166. * @final
  167. */
  168. goog.fs.DirectoryEntryImpl = function(fs, dir) {
  169. goog.fs.DirectoryEntryImpl.base(this, 'constructor', fs, dir);
  170. /**
  171. * The underlying DirectoryEntry object.
  172. *
  173. * @type {!DirectoryEntry}
  174. * @private
  175. */
  176. this.dir_ = dir;
  177. };
  178. goog.inherits(goog.fs.DirectoryEntryImpl, goog.fs.EntryImpl);
  179. /** @override */
  180. goog.fs.DirectoryEntryImpl.prototype.getFile = function(path, opt_behavior) {
  181. var d = new goog.async.Deferred();
  182. this.dir_.getFile(
  183. path, this.getOptions_(opt_behavior), goog.bind(function(entry) {
  184. d.callback(new goog.fs.FileEntryImpl(this.fs_, entry));
  185. }, this), goog.bind(function(err) {
  186. var msg = 'loading file ' + path + ' from ' + this.getFullPath();
  187. d.errback(new goog.fs.Error(err, msg));
  188. }, this));
  189. return d;
  190. };
  191. /** @override */
  192. goog.fs.DirectoryEntryImpl.prototype.getDirectory = function(
  193. path, opt_behavior) {
  194. var d = new goog.async.Deferred();
  195. this.dir_.getDirectory(
  196. path, this.getOptions_(opt_behavior), goog.bind(function(entry) {
  197. d.callback(new goog.fs.DirectoryEntryImpl(this.fs_, entry));
  198. }, this), goog.bind(function(err) {
  199. var msg = 'loading directory ' + path + ' from ' + this.getFullPath();
  200. d.errback(new goog.fs.Error(err, msg));
  201. }, this));
  202. return d;
  203. };
  204. /** @override */
  205. goog.fs.DirectoryEntryImpl.prototype.createPath = function(path) {
  206. // If the path begins at the root, reinvoke createPath on the root directory.
  207. if (goog.string.startsWith(path, '/')) {
  208. var root = this.getFileSystem().getRoot();
  209. if (this.getFullPath() != root.getFullPath()) {
  210. return root.createPath(path);
  211. }
  212. }
  213. // Filter out any empty path components caused by '//' or a leading slash.
  214. var parts = goog.array.filter(path.split('/'), goog.functions.identity);
  215. /**
  216. * @param {goog.fs.DirectoryEntryImpl} dir
  217. * @return {!goog.async.Deferred}
  218. */
  219. function getNextDirectory(dir) {
  220. if (!parts.length) {
  221. return goog.async.Deferred.succeed(dir);
  222. }
  223. var def;
  224. var nextDir = parts.shift();
  225. if (nextDir == '..') {
  226. def = dir.getParent();
  227. } else if (nextDir == '.') {
  228. def = goog.async.Deferred.succeed(dir);
  229. } else {
  230. def = dir.getDirectory(nextDir, goog.fs.DirectoryEntry.Behavior.CREATE);
  231. }
  232. return def.addCallback(getNextDirectory);
  233. }
  234. return getNextDirectory(this);
  235. };
  236. /** @override */
  237. goog.fs.DirectoryEntryImpl.prototype.listDirectory = function() {
  238. var d = new goog.async.Deferred();
  239. var reader = this.dir_.createReader();
  240. var results = [];
  241. var errorCallback = goog.bind(function(err) {
  242. var msg = 'listing directory ' + this.getFullPath();
  243. d.errback(new goog.fs.Error(err, msg));
  244. }, this);
  245. var successCallback = goog.bind(function(entries) {
  246. if (entries.length) {
  247. for (var i = 0, entry; entry = entries[i]; i++) {
  248. results.push(this.wrapEntry(entry));
  249. }
  250. reader.readEntries(successCallback, errorCallback);
  251. } else {
  252. d.callback(results);
  253. }
  254. }, this);
  255. reader.readEntries(successCallback, errorCallback);
  256. return d;
  257. };
  258. /** @override */
  259. goog.fs.DirectoryEntryImpl.prototype.removeRecursively = function() {
  260. var d = new goog.async.Deferred();
  261. this.dir_.removeRecursively(
  262. goog.bind(d.callback, d, true /* result */), goog.bind(function(err) {
  263. var msg = 'removing ' + this.getFullPath() + ' recursively';
  264. d.errback(new goog.fs.Error(err, msg));
  265. }, this));
  266. return d;
  267. };
  268. /**
  269. * Converts a value in the Behavior enum into an options object expected by the
  270. * File API.
  271. *
  272. * @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
  273. * existing files.
  274. * @return {!Object<boolean>} The options object expected by the File API.
  275. * @private
  276. */
  277. goog.fs.DirectoryEntryImpl.prototype.getOptions_ = function(opt_behavior) {
  278. if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE) {
  279. return {'create': true};
  280. } else if (opt_behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) {
  281. return {'create': true, 'exclusive': true};
  282. } else {
  283. return {};
  284. }
  285. };
  286. /**
  287. * A file in a local filesystem.
  288. *
  289. * This should not be instantiated directly. Instead, it should be accessed via
  290. * {@link goog.fs.DirectoryEntry#getFile}.
  291. *
  292. * @param {!goog.fs.FileSystem} fs The wrapped filesystem.
  293. * @param {!FileEntry} file The underlying FileEntry object.
  294. * @constructor
  295. * @extends {goog.fs.EntryImpl}
  296. * @implements {goog.fs.FileEntry}
  297. * @final
  298. */
  299. goog.fs.FileEntryImpl = function(fs, file) {
  300. goog.fs.FileEntryImpl.base(this, 'constructor', fs, file);
  301. /**
  302. * The underlying FileEntry object.
  303. *
  304. * @type {!FileEntry}
  305. * @private
  306. */
  307. this.file_ = file;
  308. };
  309. goog.inherits(goog.fs.FileEntryImpl, goog.fs.EntryImpl);
  310. /** @override */
  311. goog.fs.FileEntryImpl.prototype.createWriter = function() {
  312. var d = new goog.async.Deferred();
  313. this.file_.createWriter(function(w) {
  314. d.callback(new goog.fs.FileWriter(w));
  315. }, goog.bind(function(err) {
  316. var msg = 'creating writer for ' + this.getFullPath();
  317. d.errback(new goog.fs.Error(err, msg));
  318. }, this));
  319. return d;
  320. };
  321. /** @override */
  322. goog.fs.FileEntryImpl.prototype.file = function() {
  323. var d = new goog.async.Deferred();
  324. this.file_.file(function(f) { d.callback(f); }, goog.bind(function(err) {
  325. var msg = 'getting file for ' + this.getFullPath();
  326. d.errback(new goog.fs.Error(err, msg));
  327. }, this));
  328. return d;
  329. };