Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(220)

Side by Side Diff: ui/file_manager/file_manager/background/js/import_history.js

Issue 677213002: Add ImportHistory class and RecordStorage class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address an outstanding TODO Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8 * @constructor
9 *
10 * @param {!RecordStorage} storage
11 */
12 function ImportHistory(storage) {
13
14 /** @private {!RecordStorage} */
15 this.storage_ = storage;
16
17 /** @private {!Object.<string, !Array.<string>>} */
18 this.history_ = {};
19 };
mtomasz 2014/10/29 01:10:41 nit: ; seems redundant here.
Steve McKay 2014/10/29 16:57:21 Done.
20
21 /**
22 * Prints an error to the console.
23 *
24 * @param {!Error} error
25 * @private
26 */
27 ImportHistory.handleError_ = function(error) {
28 console.error(error.stack || error);
29 };
30
31 /**
32 * Use this factory method to get a fully ready instance of ImportHistory.
33 *
34 * @param {!RecordStorage} storage
35 *
36 * @return {!Promise.<!ImportHistory>} Resolves when history instance is ready.
37 */
38 ImportHistory.load = function(storage) {
39 var history = new ImportHistory(storage);
40 return history.reload().then(
41 function() {
42 return history;
43 });
44 };
45
46 /**
47 * Reloads history from disk. Should be called when the file
48 * is synced.
49 *
50 * @return {!Promise} Resolves when history has been loaded.
51 */
52 ImportHistory.prototype.reload = function() {
53 this.history_ = {};
54 return this.storage_.readAll()
55 .then(
56 function(entries) {
57 entries.forEach(
58 function(entry) {
59 this.updateHistoryRecord_(entry[0], entry[1]);
60 }.bind(this));
61 }.bind(this))
62 .catch(ImportHistory.handleError_);
63 };
64
65 /**
66 * Adds a history entry to the in-memory history model.
67 * @param {string} key
68 * @param {string} destination
69 */
70 ImportHistory.prototype.updateHistoryRecord_ = function(key, destination) {
71 if (key in this.history_) {
72 this.history_[key].push(destination);
73 } else {
74 this.history_[key] = [destination];
75 }
76 };
77
78 /**
79 * @param {!FileEntry} entry
80 * @param {string} destination
81 * @return {!Promise.<boolean>} Settles with true if the FileEntry
82 * was previously imported to the specified destination.
83 */
84 ImportHistory.prototype.wasImported = function(entry, destination) {
85 return this.createKey_(entry)
86 .then(
87 function(key) {
88 return this.getDestinations_(key).indexOf(destination) >= 0;
89 }.bind(this))
90 .catch(ImportHistory.handleError_);
91 };
92
93 /**
94 * @param {!FileEntry} entry
95 * @param {string} destination
96 * @return {!Promise.<boolean>} Settles with true if the FileEntry
97 * was previously imported to the specified destination.
98 */
99 ImportHistory.prototype.markImported = function(entry, destination) {
100 return this.createKey_(entry)
101 .then(this.addDestination_.bind(this, destination))
102 .catch(ImportHistory.handleError_);
103 };
104
105 /**
106 * @param {string} key
107 * @return {!Array.<string>} The list of previously noted
108 * destinations, or an empty array, if none.
109 */
110 ImportHistory.prototype.getDestinations_ = function(key) {
111 return key in this.history_ ? this.history_[key] : [];
112 };
113
114 /**
115 * @param {string} destination
116 * @param {string} key
117 * @return {!Promise}
118 */
119 ImportHistory.prototype.addDestination_ = function(destination, key) {
120 this.updateHistoryRecord_(key, destination);
121 return this.storage_.write([key, destination]);
122 };
123
124 /**
125 * @param {!FileEntry} entry
126 * @return {!Promise.<string>} Settles with a the key is available.
127 */
128 ImportHistory.prototype.createKey_ = function(fileEntry) {
129 var entry = new FileEntryWrapper(fileEntry);
130 return new Promise(
131 function(resolve, reject) {
132 entry.getMetadata()
133 .then(
134 function(metadata) {
135 if (!('modificationTime' in metadata)) {
136 reject('File entry missing "modificationTime" field.');
137 } else if (!('size' in metadata)) {
138 reject('File entry missing "size" field.');
139 } else {
140 resolve(
141 metadata['modificationTime'] + '_' + metadata['size']);
142 }
143 }.bind(this));
144 }.bind(this));
145 };
146
147 /**
148 * An simple record storage mechanism.
149 *
150 * @interface
151 */
152 function RecordStorage(entry) {};
153
154 /**
155 * Adds a new record.
156 *
157 * @param {!Array.<*>} record
158 * @return {!Promise} Resolves when record is added.
159 */
160 RecordStorage.prototype.write;
mtomasz 2014/10/29 01:10:41 Will this compile? We used to write an empty metho
Steve McKay 2014/10/29 16:57:21 This is purely in anticipation of Closure compilat
161
162 /**
163 * Reads all records.
164 *
165 * @return {!Promise.<!Array.<!Array.<*>>>}
166 */
167 RecordStorage.prototype.readAll;
168
169 /**
170 * A {@code RecordStore} that persists data in a {@code FileEntry}.
171 *
172 * @param {!FileEntry} entry
173 *
174 * @constructor
175 * @implements {RecordStorage}
176 */
177 function FileEntryRecordStorage(entry) {
178 /** @private {!FileEntryWrapper} */
179 this.entry_ = new FileEntryWrapper(entry);
180 };
181
182 /**
183 * Prints an error to the console.
184 *
185 * @param {!Error} error
186 * @private
187 */
188 FileEntryRecordStorage.handleError_ = function(error) {
189 console.error(error.stack || error);
190 };
191
192 /** @override */
193 FileEntryRecordStorage.prototype.write = function(record) {
194 // TODO(smckay): should we make an effort to reuse a file writer?
195 return this.entry_.createWriter()
196 .then(this.writeRecord_.bind(this, record))
197 .catch(FileEntryRecordStorage.handleError_);
198 };
199
200 /**
201 * Appends a new record to the end of the file.
202 *
203 * @param {!Object} record
204 * @param {!FileWriter} writer
205 * @return {!Promise} Resolves when write is complete.
206 * @private
207 */
208 FileEntryRecordStorage.prototype.writeRecord_ = function(record, writer) {
209 return new Promise(
210 function(resolve, reject) {
211 var blob = new Blob(
212 [JSON.stringify(record) + ',\n'],
213 {type: 'application/json; charset=utf-8'});
mtomasz 2014/10/29 01:10:41 It's not really a valid json. How about simply pla
Steve McKay 2014/10/29 16:57:21 Done. text/plain; charset=UTF-8
214
215 writer.onwriteend = function() {
216 resolve();
217 };
218 writer.onerror = function() {
219 reject();
220 };
221
222 writer.seek(writer.length);
223 writer.write(blob);
224 }.bind(this));
225 };
226
227 /** @override */
228 FileEntryRecordStorage.prototype.readAll = function() {
229 return this.entry_.file()
230 .then(this.readFileAsText_.bind(this), function() { return '';})
mtomasz 2014/10/29 01:10:41 nit: space after ; missing.
Steve McKay 2014/10/29 16:57:21 Done.
231 .then(this.parse_.bind(this))
232 .then(
233 function(entries) {
234 return entries;
235 })
236 .catch(FileEntryRecordStorage.handleError_);
237 };
238
239 /**
240 * Reads all lines from the entry.
241 *
242 * @param {!File} file
243 * @return {!Promise.<!Array.<string>>}
244 * @private
245 */
246 FileEntryRecordStorage.prototype.readFileAsText_ = function(file) {
247 return new Promise(
248 function(resolve, reject) {
249
250 var reader = new FileReader();
251
252 reader.onloadend = function() {
253 if (!!reader.error) {
254 FileEntryRecordStorage.handleError_(reader.error);
255 resolve('');
256 } else {
257 resolve(reader.result);
258 }
259 };
260
261 reader.onerror = function(error) {
262 FileEntryRecordStorage.handleError_(error);
263 reject(e);
264 };
265
266 reader.readAsText(file);
267 }.bind(this));
268 };
269
270 /**
271 * Parses the text.
272 *
273 * @param {string} text
274 * @return {!Promise.<!Array.<!Object>>}
275 * @private
276 */
277 FileEntryRecordStorage.prototype.parse_ = function(text) {
278 return new Promise(
279 function(resolve, reject) {
280 if (text.length == 0) {
281 resolve([]);
282 } else {
283 // Dress up the contents of the file like an array,
284 // so the JSON object can parse it using JSON.parse.
285 // That means we need to both:
286 // 1) Strip the trailing ',\n' from the last record
287 // 2) Surround the whole string in brackets.
288 // NOTE: JSON.parse is WAY faster than parsing this
289 // ourselves in javascript.
290 var json = '[' + text.substring(0, text.length - 2) + ']';
291 resolve(JSON.parse(json));
mtomasz 2014/10/29 01:10:41 Date objects will not deserialize properly from js
Steve McKay 2014/10/29 16:57:22 The value is just a string. There's no need to par
292 }
293 }.bind(this));
294 };
295
296 /**
297 * An Object storage mechanism built on top of a FileEntry.
298 *
299 * @param {!FileEntry} entry
300 *
301 * @constructor
302 * @private
303 */
304 function FileEntryWrapper(entry) {
305 /** @private {!FileEntry} */
306 this.entry_ = entry;
307 };
308
309 /**
310 * A "Promisary" wrapper around entry.getWriter.
311 * @return {!Promise.<!FileWriter>}
312 */
313 FileEntryWrapper.prototype.createWriter = function() {
314 return new Promise(this.entry_.createWriter.bind(this.entry_));
315 };
316
317 /**
318 * A "Promisary" wrapper around entry.file.
319 * @return {!Promise.<!File>}
320 */
321 FileEntryWrapper.prototype.file = function() {
322 return new Promise(this.entry_.file.bind(this.entry_));
323 };
324
325 /**
326 * @return {!Promise.<!Object>} Data format not yet verified.
327 * Either a string, or an object.
328 */
329 FileEntryWrapper.prototype.getMetadata = function() {
330 return new Promise(this.entry_.getMetadata.bind(this.entry_));
331 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698