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