a81ead30b044a994ac0d89a4d67e73ee14d5796f
[platform/core/api/webapi-plugins.git] / src / filesystem / js / file_stream.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var can_change_size = false;
18
19 function FileStream(data, mode, encoding) {
20     var _totalBytes = data.fileSize || 0;
21     var _position = mode === 'a' ? _totalBytes : 0;
22
23     Object.defineProperties(this, {
24         eof: {
25             get: function() {
26                 return _totalBytes < _position;
27             },
28             set: function(v) {},
29             enumerable: true
30         },
31         position: {
32             get: function() {
33                 return _position;
34             },
35             set: function(v) {
36                 _position = Math.max(0, v);
37                 if (can_change_size) {
38                     _totalBytes = Math.max(_position, _totalBytes);
39                 }
40             },
41             enumerable: true
42         },
43         bytesAvailable: {
44             get: function() {
45                 return this.eof ? -1 : Math.max(0, _totalBytes - _position);
46             },
47             set: function(v) {},
48             enumerable: true
49         },
50         _mode: {
51             value: mode,
52             writable: false,
53             enumerable: false
54         },
55         _encoding: {
56             value: encoding,
57             writable: false,
58             enumerable: false
59         },
60         _file: {
61             value: data,
62             writable: false,
63             enumerable: false
64         },
65         _closed: {
66             value: false,
67             writable: true,
68             enumerable: false
69         },
70         _rewrite: {
71             value: mode === 'w' ? true : false,
72             writable: true,
73             enumerable: false
74         }
75     });
76 }
77
78 function _checkClosed(stream) {
79     if (stream._closed) {
80         throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is closed.');
81     }
82 }
83
84 function closeFileStream() {
85     this._closed = true;
86 }
87
88 FileStream.prototype.close = function() {
89     closeFileStream.apply(this, arguments);
90 };
91
92 function _checkReadAccess(mode) {
93     if (mode !== 'r' && mode !== 'rw') {
94         throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is not in read mode.');
95     }
96 }
97
98 function _checkWriteAccess(mode) {
99     if (mode !== 'a' && mode !== 'w' && mode !== 'rw') {
100         throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is not in write mode.');
101     }
102 }
103
104 /* returns array of numbers */
105 function string_to_binary(str) {
106     var output = [];
107     var len = str.length;
108     var c;
109     for (var i = 0; i < len; i++) {
110         // decode unicode codepoint U+0100 as zero byte
111         c = str.charCodeAt(i);
112         output.push(c == 0x100 ? 0 : c);
113     }
114     return output;
115 }
116
117 /* receives array of numbers, returns string */
118 function binary_to_string(data) {
119     var output = '';
120     var len = data.length;
121     // endecode zero byte as unicode codepoint U+0100
122     var zero = String.fromCharCode(0x100);
123     var b;
124     for (var i = 0; i < len; i++) {
125         b = data[i] & 0xff; // conversion to octet
126         output += b == 0 ? zero : String.fromCharCode(b);
127     }
128     return output;
129 }
130
131 function read() {
132     var args = validator_.validateArgs(arguments, [
133         {
134             name: 'charCount',
135             type: types_.LONG
136         }
137     ]);
138
139     _checkClosed(this);
140     _checkReadAccess(this._mode);
141
142     if (!arguments.length) {
143         throw new WebAPIException(
144             WebAPIException.INVALID_VALUES_ERR,
145             'Argument "charCount" missing'
146         );
147     }
148     if (!type_.isNumber(args.charCount)) {
149         throw new WebAPIException(
150             WebAPIException.TYPE_MISMATCH_ERR,
151             'Argument "charCount" must be a number'
152         );
153     }
154     if (args.charCount <= 0) {
155         throw new WebAPIException(
156             WebAPIException.INVALID_VALUES_ERR,
157             'Argument "charCount" must be greater than 0'
158         );
159     }
160     if (this.eof) {
161         throw new WebAPIException(WebAPIException.IO_ERR, 'Stream is marked as EOF.');
162     }
163
164     var _count = this.bytesAvailable;
165
166     var data = {
167         location: commonFS_.toRealPath(this._file.fullPath),
168         encoding: this._encoding,
169         offset: this.position || 0,
170         length: args.charCount > _count ? _count : args.charCount
171     };
172
173     var result = native_.callSync('File_readString', data);
174     if (native_.isFailure(result)) {
175         throw new WebAPIException(WebAPIException.IO_ERR, 'Could not read');
176     }
177     var outData = native_.getResultObject(result);
178
179     if (outData.length) {
180         can_change_size = true;
181         this.position += outData.length;
182         can_change_size = false;
183     } else {
184         this.position += 1; // Set EOF
185     }
186
187     return outData;
188 }
189
190 FileStream.prototype.read = function() {
191     return read.apply(this, arguments);
192 };
193
194 function readBytes() {
195     var args = validator_.validateArgs(arguments, [
196         {
197             name: 'byteCount',
198             type: types_.LONG
199         }
200     ]);
201
202     _checkClosed(this);
203     _checkReadAccess(this._mode);
204
205     if (args.byteCount <= 0) {
206         throw new WebAPIException(
207             WebAPIException.INVALID_VALUES_ERR,
208             'Argument "byteCount" must be greater than 0'
209         );
210     }
211
212     var _count = this.bytesAvailable;
213
214     var data = {
215         location: commonFS_.toRealPath(this._file.fullPath),
216         offset: this.position || 0,
217         length: args.byteCount > _count ? _count : args.byteCount
218     };
219
220     var result = native_.callSync('File_readBytes', data);
221     if (native_.isFailure(result)) {
222         throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR, 'Could not read');
223     }
224
225     var decoded = string_to_binary(native_.getResultObject(result));
226
227     if (decoded.length) {
228         can_change_size = true;
229         this.position += decoded.length;
230         can_change_size = false;
231     } else {
232         this.position += 1; // Set EOF
233     }
234
235     return decoded;
236 }
237
238 FileStream.prototype.readBytes = function() {
239     return readBytes.apply(this, arguments);
240 };
241
242 FileStream.prototype.readBase64 = function() {
243     return base64_encode(readBytes.apply(this, arguments));
244 };
245
246 function write() {
247     var args = validator_.validateArgs(arguments, [
248         {
249             name: 'stringData',
250             type: types_.STRING
251         }
252     ]);
253
254     _checkClosed(this);
255     _checkWriteAccess(this._mode);
256
257     if (!arguments.length) {
258         throw new WebAPIException(
259             WebAPIException.NOT_FOUND_ERR,
260             'Argument "stringData" missing'
261         );
262     }
263
264     var data = {
265         location: commonFS_.toRealPath(this._file.fullPath),
266         encoding: this._encoding,
267         offset: this.position,
268         data: args.stringData,
269         rewrite: this._rewrite
270     };
271
272     var result = native_.callSync('File_writeString', data);
273
274     if (native_.isFailure(result)) {
275         throw new WebAPIException(WebAPIException.IO_ERR, 'Could not write');
276     }
277     can_change_size = true;
278     this.position = this.position + args.stringData.length;
279     can_change_size = false;
280     this._rewrite = false;
281 }
282
283 FileStream.prototype.write = function() {
284     write.apply(this, arguments);
285 };
286
287 function writeBytes() {
288     var args = validator_.validateArgs(arguments, [
289         {
290             name: 'byteData',
291             type: types_.ARRAY,
292             values: undefined /* was types_.OCTET, but checking moved
293                                 to binary_to_string for performance */
294         }
295     ]);
296
297     _checkClosed(this);
298     _checkWriteAccess(this._mode);
299
300     if (!arguments.length) {
301         throw new WebAPIException(
302             WebAPIException.TYPE_MISMATCH_ERR,
303             'Argument "byteData" missing'
304         );
305     }
306
307     var data = {
308         location: commonFS_.toRealPath(this._file.fullPath),
309         offset: this.position,
310         data: binary_to_string(args.byteData),
311         rewrite: this._rewrite
312     };
313
314     var result = native_.callSync('File_writeBytes', data);
315
316     if (native_.isFailure(result)) {
317         throw new WebAPIException(WebAPIException.IO_ERR, 'Could not write');
318     }
319     can_change_size = true;
320     this.position = this.position + args.byteData.length;
321     can_change_size = false;
322     this._rewrite = false;
323 }
324
325 FileStream.prototype.writeBytes = function() {
326     writeBytes.apply(this, arguments);
327 };
328
329 function writeBase64() {
330     var args = validator_.validateArgs(arguments, [
331         {
332             name: 'base64Data',
333             type: types_.STRING
334         }
335     ]);
336
337     _checkClosed(this);
338     _checkWriteAccess(this._mode);
339
340     var data = {
341         location: commonFS_.toRealPath(this._file.fullPath),
342         offset: this.position,
343         data: args.base64Data,
344         rewrite: this._rewrite
345     };
346
347     var result = native_.callSync('File_writeBase64', data);
348
349     if (native_.isFailure(result)) {
350         throw native_.getErrorObject(result);
351     }
352
353     var written_bytes = native_.getResultObject(result);
354
355     can_change_size = true;
356     this.position += written_bytes;
357     can_change_size = false;
358     this._rewrite = false;
359 }
360
361 FileStream.prototype.writeBase64 = function() {
362     writeBase64.apply(this, arguments);
363 };