Release 4.0.0-preview1-00051
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.DataControl / Tizen.Applications.DataControl / MatrixCursor.cs
1 /*
2  * Copyright (c) 2017 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 using System;
18 using System.IO;
19 using System.Text;
20 using System.Collections.Generic;
21 using System.Threading;
22 using System.Diagnostics;
23
24 namespace Tizen.Applications.DataControl
25 {
26     /// <summary>
27     /// Represents MatrixCursor class for DataControl provider's matrix cursor.
28     /// </summary>
29     public class MatrixCursor : IDisposable, ICursor
30     {
31         private const string LogTag = "Tizen.Applications.DataControl";
32         private FileStream _fs;
33         private bool _disposed = false;
34         private string _cursorPath;
35         private long _rowCount = 0;
36         private long _rowCountPosition = 0;
37         private int _currentRowIndex = 0;
38         private IList<long> _rowFieldOffset = new List<long>();
39         private string[] _columnNames;
40         private ColumnType[] _columnTypes;
41         private const int ColumnTypeNull = 5;
42
43         private byte[] GetValue(int index)
44         {
45             byte[] int_tmp = new byte[sizeof(int)];
46             byte[] ret_array;
47             ColumnType type;
48             int size, read_len;
49
50             MoveToColumn(index);
51
52             read_len = _fs.Read(int_tmp, 0, int_tmp.Length);
53             if (read_len != int_tmp.Length)
54             {
55                 ErrorFactory.ThrowException(ResultType.IoError, true, "Column Type " + index.ToString());
56             }
57
58             type = (ColumnType)BitConverter.ToInt32(int_tmp, 0);
59
60             if (type != _columnTypes[index])
61             {
62                 if ((int)type == ColumnTypeNull &&
63                     (_columnTypes[index] == ColumnType.ColumnTypeBlob || _columnTypes[index] == ColumnType.ColumnTypeString))
64                 {
65                     return null; /* null type */
66                 }
67
68                 ErrorFactory.ThrowException(ResultType.IoError, true, "Type mismatch " + index.ToString());
69             }
70
71             read_len = _fs.Read(int_tmp, 0, int_tmp.Length);
72             if (read_len != int_tmp.Length)
73             {
74                 ErrorFactory.ThrowException(ResultType.IoError, true, "Column size " + index.ToString());
75             }
76
77             size = BitConverter.ToInt32(int_tmp, 0);
78
79             if (size < 0)
80             {
81                 ErrorFactory.ThrowException(ResultType.IoError, true, "Invalid data size " + index.ToString());
82             }
83
84             ret_array = new byte[size];
85             read_len = _fs.Read(ret_array, 0, ret_array.Length);
86             if (read_len != ret_array.Length)
87             {
88                 ErrorFactory.ThrowException(ResultType.IoError, true, "Column value size " + index.ToString());
89                 return null;
90             }
91
92             return ret_array;
93         }
94
95         private void MoveToColumn(int ColumnIndex)
96         {
97             int i, tmp_position;
98             byte[] int_tmp = new byte[sizeof(int)];
99             int read_len;
100             long seek_len;
101
102             seek_len = _fs.Seek(_rowFieldOffset[_currentRowIndex], SeekOrigin.Begin);
103             if (seek_len != _rowFieldOffset[_currentRowIndex])
104             {
105                 ErrorFactory.ThrowException(ResultType.IoError, true, "Row index " + _currentRowIndex.ToString());
106             }
107
108             for (i = 0; i < ColumnIndex; i++)
109             {
110                 /* type(int) size(int) value */
111                 switch (_columnTypes[i])
112                 {
113                     case ColumnType.ColumnTypeInt:
114                         tmp_position = sizeof(int) * 2 + sizeof(Int64);
115                         _fs.Seek(tmp_position, SeekOrigin.Current);
116                         break;
117                     case ColumnType.ColumnTypeDouble:
118                         tmp_position = sizeof(int) * 2 + sizeof(double);
119                         _fs.Seek(tmp_position, SeekOrigin.Current);
120                         break;
121                     case ColumnType.ColumnTypeString:
122                         tmp_position = sizeof(int);
123                         _fs.Seek(tmp_position, SeekOrigin.Current);
124                         read_len = _fs.Read(int_tmp, 0, int_tmp.Length);
125                         if (read_len != int_tmp.Length)
126                         {
127                             ErrorFactory.ThrowException(ResultType.IoError, true, "Column Index " + ColumnIndex.ToString());
128                         }
129
130                         tmp_position = BitConverter.ToInt32(int_tmp, 0);
131
132                         if (tmp_position > 0)
133                         {
134                             _fs.Seek(tmp_position, SeekOrigin.Current);
135                         }
136
137                         break;
138                     case ColumnType.ColumnTypeBlob:
139                         tmp_position = sizeof(int);
140                         _fs.Seek(tmp_position, SeekOrigin.Current);
141
142                         read_len = _fs.Read(int_tmp, 0, int_tmp.Length);
143                         if (read_len != int_tmp.Length)
144                         {
145                             ErrorFactory.ThrowException(ResultType.IoError, true, "Column Index " + ColumnIndex.ToString());
146                         }
147
148                         tmp_position = BitConverter.ToInt32(int_tmp, 0);
149
150                         if (tmp_position > 0)
151                         {
152                             _fs.Seek(tmp_position, SeekOrigin.Current);
153                         }
154
155                         break;
156                 }
157             }
158
159         }
160
161         internal FileStream GetFileStream()
162         {
163             return _fs;
164         }
165
166         /// <summary>
167         /// Gets column count of MatrixCursor.
168         /// </summary>
169         public int GetColumnCount()
170         {
171             return _columnTypes.Length;
172         }
173
174         /// <summary>
175         /// Returns the column type at the given zero-based column index.
176         /// </summary>
177         /// <param name="index">Target column index</param>
178         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
179         public ColumnType GetColumnType(int index)
180         {
181             if (index < 0 || index >= _columnTypes.Length)
182             {
183                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
184             }
185
186             return _columnTypes[index];
187         }
188
189         /// <summary>
190         /// Returns the column name at the given zero-based column index.
191         /// </summary>
192         /// <param name="index">Target column index</param>
193         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
194         public string GetColumnName(int index)
195         {
196             if (index < 0 || index >= _columnTypes.Length)
197             {
198                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
199             }
200
201             return _columnNames[index];
202         }
203
204         /// <summary>
205         /// Gets MatrixCursor's row count.
206         /// </summary>
207         public long GetRowCount()
208         {
209             return _rowCount;
210         }
211
212         /// <summary>
213         /// Move the MatrixCursor to the next row.
214         /// </summary>
215         public bool Next()
216         {
217             if (_currentRowIndex >= _rowCount - 1)
218             {
219                 return false;
220             }
221
222             _currentRowIndex++;
223             return true;
224         }
225
226         /// <summary>
227         /// Move the MatrixCursor to the previous row.
228         /// </summary>
229         public bool Prev()
230         {
231             if (_currentRowIndex <= 0)
232             {
233                 return false;
234             }
235
236             _currentRowIndex--;
237             return true;
238         }
239
240         /// <summary>
241         /// Move the MatrixCursor to the first row.
242         /// </summary>
243         public bool Reset()
244         {
245             _currentRowIndex = 0;
246             return true;
247         }
248
249         /// <summary>
250         /// Returns the value of the requested column as a int.
251         /// </summary>
252         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
253         public int GetIntValue(int index)
254         {
255             int ret;
256             byte[] byte_array;
257
258             if (index < 0 || index >= _columnTypes.Length)
259             {
260                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
261             }
262
263             byte_array = GetValue(index);
264             if (byte_array == null)
265             {
266                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
267             }
268             ret = BitConverter.ToInt32(byte_array, 0);
269
270             return ret;
271         }
272
273         /// <summary>
274         /// Returns the value of the requested column as a int64.
275         /// </summary>
276         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
277         public Int64 GetInt64Value(int index)
278         {
279             Int64 ret;
280             byte[] byte_array;
281
282             if (index < 0 || index >= _columnTypes.Length)
283             {
284                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
285             }
286
287             byte_array = GetValue(index);
288             if (byte_array == null)
289             {
290                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
291             }
292             ret = BitConverter.ToInt64(byte_array, 0);
293
294             return ret;
295         }
296
297         /// <summary>
298         /// Returns the value of the requested column as a double.
299         /// </summary>
300         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
301         public double GetDoubleValue(int index)
302         {
303             double ret;
304             byte[] byte_array;
305
306             if (index < 0 || index >= _columnTypes.Length)
307             {
308                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
309             }
310
311             byte_array = GetValue(index);
312             if (byte_array == null)
313             {
314                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
315             }
316             ret = BitConverter.ToDouble(byte_array, 0);
317
318             return ret;
319         }
320
321         /// <summary>
322         /// Returns the value of the requested column as a string.
323         /// </summary>
324         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
325         public string GetStringValue(int index)
326         {
327             string ret;
328             byte[] byte_array;
329
330             if (index < 0 || index >= _columnTypes.Length)
331             {
332                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
333             }
334
335             byte_array = GetValue(index);
336
337             if (byte_array == null)
338             {
339                 return null;
340             }
341
342             ret = Encoding.UTF8.GetString(byte_array).TrimEnd('\0');
343             return ret;
344
345         }
346
347         /// <summary>
348         /// Returns the value of the requested column as a blob.
349         /// </summary>
350         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
351         public byte[] GetBlobValue(int index)
352         {
353             byte[] byte_array;
354
355             if (index < 0 || index >= _columnTypes.Length)
356             {
357                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
358             }
359
360             byte_array = GetValue(index);
361             return byte_array;
362         }
363
364         private static class FileManager
365         {
366             private static readonly string DATACONTROL_DIRECTORY = "/tmp/";
367             private static Dictionary<int, int> fileTable = new Dictionary<int, int>();
368             public static string OpenFileStream(int threadID)
369             {
370                 string path;
371                 int index;
372
373                 if (threadID < 0)
374                 {
375                     Log.Error(LogTag, "threadID is " + threadID.ToString());
376                     return null;
377                 }
378
379                 if (fileTable.ContainsKey(threadID) == false)
380                 {
381                     fileTable.Add(threadID, 0);
382                 }
383
384                 index = fileTable[threadID];
385                 index++;
386                 fileTable[threadID] = index;
387
388                 path = DATACONTROL_DIRECTORY + Application.Current.ApplicationInfo.ApplicationId + "_" + Process.GetCurrentProcess().Id.ToString() + "_" + threadID.ToString() + "_" + index.ToString();
389
390                 return path;
391             }
392         }
393
394         /// <summary>
395         /// Initializes MatrixCursor class with columnNames and columnTypes.
396         /// </summary>
397         /// <param name="columnNames">MatrixCursor's column name list</param>
398         /// <param name="columnTypes">MatrixCursor's column type list</param>
399         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
400         ///  <exception cref="InvalidOperationException">Thrown in case of any internal error.</exception>
401         public MatrixCursor(string[] columnNames, ColumnType[] columnTypes)
402         {
403             byte[] byte_tmp, length_tmp, string_tmp;
404             int i, total_len_of_column_names = 0;
405
406             if (columnNames == null || columnTypes == null ||
407                 (columnNames.Length != columnTypes.Length) || columnNames.Length < 1)
408             {
409                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
410             }
411
412             for (i = 0; i < columnNames.Length; i++)
413             {
414                 if (string.IsNullOrEmpty(columnNames[i]))
415                 {
416                     ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "columnNames index " + i.ToString());
417                 }
418             }
419
420             for (i = 0; i < columnTypes.Length; i++)
421             {
422                 if ( columnTypes[i] < ColumnType.ColumnTypeInt || columnTypes[i] > ColumnType.ColumnTypeBlob)
423                 {
424                     ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "columnTypes index" + i.ToString());
425                 }
426             }
427
428             _columnNames = columnNames;
429             _columnTypes = columnTypes;
430
431             _cursorPath = FileManager.OpenFileStream(Thread.CurrentThread.ManagedThreadId);
432             if (_cursorPath == null)
433             {
434                 Log.Error(LogTag, "Unable to create a cursor file : " + _cursorPath);
435                 ErrorFactory.ThrowException(ResultType.IoError, true);
436             }
437
438             _fs = new FileStream(_cursorPath, FileMode.Create);
439             /* column count */
440             byte_tmp = BitConverter.GetBytes(columnNames.Length);
441             _fs.Write(byte_tmp, 0, byte_tmp.Length);
442
443             /* column type */
444             for (i = 0; i < columnTypes.Length; i++)
445             {
446                 byte_tmp = BitConverter.GetBytes((int)_columnTypes[i]);
447                 _fs.Write(byte_tmp, 0, byte_tmp.Length);
448             }
449
450             /* column name */
451             for (i = 0; i < columnTypes.Length; i++)
452             {
453                 string_tmp = Encoding.UTF8.GetBytes(columnNames[i]);
454                 byte_tmp = new byte[string_tmp.Length + 1];/*insert null */
455
456                 string_tmp.CopyTo(byte_tmp, 0);
457
458                 length_tmp = BitConverter.GetBytes(byte_tmp.Length);
459                 total_len_of_column_names += length_tmp.Length;
460
461                 _fs.Write(length_tmp, 0, length_tmp.Length);
462                 _fs.Write(byte_tmp, 0, byte_tmp.Length);
463             }
464
465             /* total length of column names */
466             byte_tmp = BitConverter.GetBytes(total_len_of_column_names);
467             _fs.Write(byte_tmp, 0, byte_tmp.Length);
468
469             _rowCountPosition = _fs.Position;
470             /* row count */
471             byte_tmp = BitConverter.GetBytes(_rowCount);
472             _fs.Write(byte_tmp, 0, byte_tmp.Length);
473             _fs.Flush();
474         }
475
476         internal MatrixCursor()
477         {
478             _columnNames = new string[0];
479             _columnTypes = new ColumnType[0];
480             _fs = null;
481             _cursorPath = null;
482         }
483
484         /// <summary>
485         /// Adds a new row to the end with the given column values.
486         /// </summary>
487         /// <param name="columnValues">New column values</param>
488         /// <exception cref="ArgumentException">Thrown in case of Invalid parmaeter.</exception>
489         public void AddRow(object[] columnValues)
490         {
491             int i, size = 0;
492             byte[] type_array, length_array, value_array = null, string_array, byte_tmp;
493
494             if (columnValues == null || columnValues.Length <= 0 || columnValues.Length != _columnTypes.Length)
495             {
496                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false);
497             }
498
499             using (MemoryStream ms = new MemoryStream())
500             {
501                 for (i = 0; i < _columnTypes.Length; i++)
502                 {
503                     type_array = BitConverter.GetBytes((int)_columnTypes[i]);
504                     switch (_columnTypes[i])
505                     {
506                         case ColumnType.ColumnTypeInt:
507                             if (!(columnValues[i] is Int64) && !(columnValues[i] is Int32))
508                             {
509                                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "Type mismatch :Index "  + i.ToString());
510                             }
511
512                             value_array = BitConverter.GetBytes(Convert.ToUInt64(columnValues[i]));
513                             size = value_array.Length;
514                             break;
515                         case ColumnType.ColumnTypeDouble:
516                             if (!(columnValues[i] is Double))
517                             {
518                                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "Type mismatch :Index " + i.ToString());
519                             }
520
521                             value_array = BitConverter.GetBytes(Convert.ToDouble(columnValues[i]));
522                             size = value_array.Length;
523                             break;
524                         case ColumnType.ColumnTypeString:
525                             if (columnValues[i] == null)
526                             {
527                                 type_array = BitConverter.GetBytes(ColumnTypeNull);
528                                 size = 0;
529                                 break;
530                             }
531
532                             if (!(columnValues[i] is string))
533                             {
534                                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "Type mismatch :Index " + i.ToString());
535                             }
536
537                             string_array = Encoding.UTF8.GetBytes(Convert.ToString(columnValues[i]));
538                             value_array = new byte[string_array.Length + 1];/*insert null */
539                             string_array.CopyTo(value_array, 0);
540                             size = value_array.Length;
541                             break;
542
543                         case ColumnType.ColumnTypeBlob:
544                             if (columnValues[i] == null)
545                             {
546                                 type_array = BitConverter.GetBytes(ColumnTypeNull);
547                                 size = 0;
548                                 break;
549                             }
550
551                             if (!(columnValues[i] is byte[]))
552                             {
553                                 ErrorFactory.ThrowException(ResultType.InvalidParameter, false, "Type mismatch :Index " + i.ToString());
554                             }
555
556                             value_array = (byte[])columnValues[i];
557                             size = value_array.Length;
558                             break;
559                     }
560
561                     ms.Write(type_array, 0, type_array.Length);
562
563                     length_array = BitConverter.GetBytes(size);
564                     ms.Write(length_array, 0, length_array.Length);
565                     if (size > 0)
566                     {
567                         ms.Write(value_array, 0, value_array.Length);
568                     }
569                 }
570
571                 /* update row count */
572                 _rowCount++;
573                 byte_tmp = BitConverter.GetBytes(_rowCount);
574                 _fs.Seek(_rowCountPosition, SeekOrigin.Begin);
575                 _fs.Write(byte_tmp, 0, byte_tmp.Length);
576
577                 _fs.Seek(0, SeekOrigin.End);
578
579                 _rowFieldOffset.Add(_fs.Position);
580                 ms.WriteTo(_fs);/* row data */
581                 _fs.Flush();
582
583                 Log.Debug(LogTag, "_fs pos = " + _fs.Position.ToString());
584                 Log.Debug(LogTag, "_fs len = " + _fs.Length.ToString());
585             }
586         }
587
588         /// <summary>
589         /// Releases all resources used by the MatrixCursor class.
590         /// </summary>
591         public void Dispose()
592         {
593             Dispose(true);
594         }
595
596         protected virtual void Dispose(bool disposing)
597         {
598             if (!_disposed)
599             {
600                 if (!string.IsNullOrEmpty(_cursorPath))
601                 {
602                     FileInfo fi = new FileInfo(_cursorPath);
603
604                     if (_fs != null)
605                     {
606                         _fs.Dispose();
607                     }
608
609                     if (fi.Exists)
610                     {
611                         fi.Delete();
612                     }
613                 }
614
615                 _disposed = true;
616             }
617
618             if (disposing)
619             {
620                 GC.SuppressFinalize(this);
621             }
622         }
623
624         ~MatrixCursor()
625         {
626             Dispose(false);
627         }
628     }
629 }