Fix to assign string.Empty when Marshal.PtrToStringAnsi(XXXptr)'s XXXptr is IntPtr...
[platform/core/csapi/tizenfx.git] / src / Tizen.Network.IoTConnectivity / Tizen.Network.IoTConnectivity / ResourceQuery.cs
1  /*
2  * Copyright (c) 2016 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
18 using System;
19 using System.Collections;
20 using System.Collections.Generic;
21 using System.Runtime.InteropServices;
22
23 namespace Tizen.Network.IoTConnectivity
24 {
25     /// <summary>
26     /// This class provides APIs to manage query of request.
27     /// </summary>
28     public class ResourceQuery : IDictionary<string, string>, IDisposable
29     {
30         internal const int QueryMaxLenth = 64;
31         internal IntPtr _resourceQueryHandle = IntPtr.Zero;
32         private readonly IDictionary<string, string> _query = new Dictionary<string, string>();
33         private bool _disposed = false;
34
35         /// <summary>
36         /// The resource query constructor
37         /// </summary>
38         /// <seealso cref="Add()"/>
39         /// <seealso cref="Remove()"/>
40         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
41         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
42         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
43         /// <code>
44         /// ResourceQuery query = new ResourceQuery();
45         /// </code>
46         public ResourceQuery()
47         {
48             int ret = Interop.IoTConnectivity.Common.Query.Create(out _resourceQueryHandle);
49             if (ret != (int)IoTConnectivityError.None)
50             {
51                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to create query");
52                 throw IoTConnectivityErrorFactory.GetException(ret);
53             }
54         }
55
56         internal ResourceQuery(IntPtr resourceQueryHandleToClone)
57         {
58             int ret = Interop.IoTConnectivity.Common.Query.Create(out _resourceQueryHandle);
59             if (ret != (int)IoTConnectivityError.None)
60             {
61                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to create query");
62                 throw IoTConnectivityErrorFactory.GetException(ret);
63             }
64
65             Interop.IoTConnectivity.Common.Query.QueryCallback forEachCallback = (string key, string value, IntPtr userData) =>
66             {
67                 Add(key, value);
68                 return true;
69             };
70
71             ret = Interop.IoTConnectivity.Common.Query.Foreach(resourceQueryHandleToClone, forEachCallback, IntPtr.Zero);
72             if (ret != (int)IoTConnectivityError.None)
73             {
74                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to iterate query");
75                 throw IoTConnectivityErrorFactory.GetException(ret);
76             }
77         }
78
79         /// <summary>
80         /// Destructor of the ResourceQuery class.
81         /// </summary>
82         ~ResourceQuery()
83         {
84             Dispose(false);
85         }
86
87         /// <summary>
88         /// Gets and sets the resource type of the query
89         /// </summary>
90         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
91         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
92         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
93         /// <code>
94         /// ResourceQuery query = new ResourceQuery();
95         /// query.Type = "org.tizen.light";
96         /// Console.WriteLine("Type of query : {0}", query.Type);
97         /// </code>
98         public string Type
99         {
100             get
101             {
102                 IntPtr type;
103                 int ret = Interop.IoTConnectivity.Common.Query.GetResourceType(_resourceQueryHandle, out type);
104                 if (ret != (int)IoTConnectivityError.None)
105                 {
106                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get type");
107                     return "";
108                 }
109                 return (type != IntPtr.Zero) ? Marshal.PtrToStringAnsi(type) : string.Empty;
110             }
111             set
112             {
113                 int ret = (int)IoTConnectivityError.InvalidParameter;
114                 if (ResourceTypes.IsValid(value))
115                     ret = Interop.IoTConnectivity.Common.Query.SetResourceType(_resourceQueryHandle, value);
116
117                 if (ret != (int)IoTConnectivityError.None)
118                 {
119                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set type");
120                     throw IoTConnectivityErrorFactory.GetException(ret);
121                 }
122             }
123         }
124
125         /// <summary>
126         /// Gets and sets the resource interface of the query
127         /// </summary>
128         /// <remarks>
129         /// Setter value could be a value such as <see cref="ResourceInterfaces.DefaultInterface"/>
130         /// </remarks>
131         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
132         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
133         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
134         /// <code>
135         /// ResourceQuery query = new ResourceQuery();
136         /// query.Interface = ResourceInterfaces.LinkInterface;
137         /// </code>
138         public string Interface
139         {
140             get
141             {
142                 IntPtr iface;
143                 int ret = Interop.IoTConnectivity.Common.Query.GetInterface(_resourceQueryHandle, out iface);
144                 if (ret != (int)IoTConnectivityError.None)
145                 {
146                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get interface");
147                     return "";
148                 }
149                 return (iface != IntPtr.Zero) ? Marshal.PtrToStringAnsi(iface) : string.Empty;
150             }
151             set
152             {
153                 int ret = (int)IoTConnectivityError.InvalidParameter;
154                 if (ResourceInterfaces.IsValid(value))
155                     ret = Interop.IoTConnectivity.Common.Query.SetInterface(_resourceQueryHandle, value);
156
157                 if (ret != (int)IoTConnectivityError.None)
158                 {
159                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set interface");
160                     throw IoTConnectivityErrorFactory.GetException(ret);
161                 }
162             }
163         }
164
165         /// <summary>
166         /// Contains all the query keys
167         /// </summary>
168         /// <code>
169         /// ResourceQuery query = new ResourceQuery();
170         /// query.Add("key", "value");
171         /// query.Add("newKey", "sample value");
172         /// var keys = query.Keys;
173         /// Console.WriteLine("Resource query contains keys {0} and {1}", keys.ElementAt(0), keys.ElementAt(1));
174         /// </code>
175         public ICollection<string> Keys
176         {
177             get
178             {
179                 return _query.Keys;
180             }
181         }
182
183         /// <summary>
184         /// Contains all the query values
185         /// </summary>
186         /// <code>
187         /// ResourceQuery query = new ResourceQuery();
188         /// query.Add("key", "value");
189         /// query.Add("newKey", "sample value");
190         /// var values = query.Values;
191         /// Console.WriteLine("Resource query contains values {0} and {1}", values.ElementAt(0), values.ElementAt(1));
192         /// </code>
193         public ICollection<string> Values
194         {
195             get
196             {
197                 return _query.Values;
198             }
199         }
200
201         /// <summary>
202         /// Gets the number of query elements
203         /// </summary>
204         /// <code>
205         /// ResourceQuery query = new ResourceQuery();
206         /// query.Add("key", "value");
207         /// query.Add("newKey", "sample value");
208         /// var count = query.Count;
209         /// Console.WriteLine("There are {0} keys in the query object", count);
210         /// </code>
211         public int Count
212         {
213             get
214             {
215                 return _query.Count;
216             }
217         }
218
219         /// <summary>
220         /// Represents whether the collection is readonly
221         /// </summary>
222         /// <code>
223         /// ResourceQuery query = new ResourceQuery();
224         /// if (query.IsReadOnly)
225         ///     Console.WriteLine("Read only query");
226         /// </code>
227         public bool IsReadOnly
228         {
229             get
230             {
231                 return _query.IsReadOnly;
232             }
233         }
234
235         /// <summary>
236         /// Gets or sets the query data
237         /// </summary>
238         /// <param name="key">The query key to get or set.</param>
239         /// <returns>The query with the specified key.</returns>
240         /// <code>
241         /// ResourceQuery query = new ResourceQuery();
242         /// query["key1"] = "sample-data";
243         /// Console.WriteLine("query has : {0}", query["key1"]);
244         /// </code>
245         public string this[string key]
246         {
247             get
248             {
249                 return _query[key];
250             }
251
252             set
253             {
254                 Add(key, value);
255             }
256         }
257
258         /// <summary>
259         /// Checks whether the given key exists in Query collection
260         /// </summary>
261         /// <param name="key">The key to look for</param>
262         /// <returns>true if exists. Otherwise, false</returns>
263         /// <code>
264         /// ResourceQuery query = new ResourceQuery();
265         /// query.Add("key1", "value1");
266         /// if (query.ContainsKey("key1"))
267         ///     Console.WriteLine("query conatins key : key1");
268         /// </code>
269         public bool ContainsKey(string key)
270         {
271             return _query.ContainsKey(key);
272         }
273
274         /// <summary>
275         /// Adds a new key and correspoding value into the query.
276         /// </summary>
277         /// <remarks>
278         /// The full length of query should be less than or equal to 64.
279         /// </remarks>
280         /// <param name="key">The key of the query to insert</param>
281         /// <param name="value">The string data to insert into the query</param>
282         /// <seealso cref="Remove()"/>
283         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
284         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
285         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
286         /// <code>
287         /// ResourceQuery query = new ResourceQuery();
288         /// query.Add("key1", "value1");
289         /// </code>
290         public void Add(string key, string value)
291         {
292             if (CanAddQuery(key, value))
293             {
294                 int ret = Interop.IoTConnectivity.Common.Query.Add(_resourceQueryHandle, key, value);
295                 if (ret != (int)IoTConnectivityError.None)
296                 {
297                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to add query");
298                     throw IoTConnectivityErrorFactory.GetException(ret);
299                 }
300                 _query.Add(key, value);
301             }
302             else
303             {
304                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Query cannot be added");
305                 throw IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.InvalidParameter);
306             }
307         }
308
309         /// <summary>
310         /// Removes the key and its associated value from the query.
311         /// </summary>
312         /// <param name="key">The id of the query to delete</param>
313         /// <returns>True if operation is successful. Otherwise, false</returns>
314         /// <seealso cref="Add()"/>
315         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
316         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
317         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
318         /// <code>
319         /// ResourceQuery query = new ResourceQuery();
320         /// query.Add("key1", "value1");
321         /// var result = query.Remove("key1");
322         /// </code>
323         public bool Remove(string key)
324         {
325             bool isRemoved = _query.Remove(key);
326             if (isRemoved)
327             {
328                 int ret = Interop.IoTConnectivity.Common.Query.Remove(_resourceQueryHandle, key);
329                 if (ret != (int)IoTConnectivityError.None)
330                 {
331                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to remove query");
332                     throw IoTConnectivityErrorFactory.GetException(ret);
333                 }
334             }
335             return isRemoved;
336         }
337
338         /// <summary>
339         /// Gets the value associated with the specified key.
340         /// </summary>
341         /// <param name="key">The query key</param>
342         /// <param name="value">Value corresponding to query key</param>
343         /// <returns>True if the key exists, false otherwise</returns>
344         /// <code>
345         /// ResourceQuery query = new ResourceQuery();
346         /// query.Add("key1", "value1");
347         /// string value;
348         /// var isPresent = query.TryGetValue("key1", out value);
349         /// if (isPresent)
350         ///     Console.WriteLine("value : {0}", value);
351         /// </code>
352         public bool TryGetValue(string key, out string value)
353         {
354             return _query.TryGetValue(key, out value);
355         }
356
357         /// <summary>
358         ///  Adds query key and value as a key value pair
359         /// </summary>
360         /// <param name="item">The key value pair</param>
361         /// <seealso cref="Remove()"/>
362         /// <code>
363         /// ResourceQuery query = new ResourceQuery();
364         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
365         /// </code>
366         public void Add(KeyValuePair<string, string> item)
367         {
368             Add(item.Key, item.Value);
369         }
370
371         /// <summary>
372         /// Clears the Query collection
373         /// </summary>
374         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
375         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
376         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
377         /// <code>
378         /// ResourceQuery query = new ResourceQuery();
379         /// query.Add("key1", "value1");
380         /// query.Add("key2", "value2");
381         /// query.Clear();
382         /// </code>
383         public void Clear()
384         {
385             foreach (string key in _query.Keys)
386             {
387                 int ret = Interop.IoTConnectivity.Common.Query.Remove(_resourceQueryHandle, key);
388                 if (ret != (int)IoTConnectivityError.None)
389                 {
390                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to clear query");
391                     throw IoTConnectivityErrorFactory.GetException(ret);
392                 }
393             }
394             _query.Clear();
395         }
396
397         /// <summary>
398         /// Checks if the given query pair exists
399         /// </summary>
400         /// <param name="item">The key value pair</param>
401         /// <returns>True if exists. Otherwise, false</returns>
402         /// <code>
403         /// ResourceQuery query = new ResourceQuery();
404         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
405         /// var isPresent = query.Contains(new KeyValuePair<string, string>("key1", "value1"));
406         /// if (isPresent)
407         ///     Console.WriteLine("Key value pair is present");
408         /// </code>
409         public bool Contains(KeyValuePair<string, string> item)
410         {
411             return _query.Contains(item);
412         }
413
414         /// <summary>
415         /// Copies the elements of the query collection to an Array, starting at a particular index.
416         /// </summary>
417         /// <param name="array">The destination array</param>
418         /// <param name="arrayIndex">Index parameter</param>
419         /// <code>
420         /// ResourceQuery query = new ResourceQuery();
421         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
422         /// KeyValuePair<string, string>[] dest = new KeyValuePair<string, string>[query.Count];
423         /// query.CopyTo(dest, 0);
424         /// Console.WriteLine("Dest conatins ({0}, {1})", dest[0].Key, dest[0].Value);
425         /// </code>
426         public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
427         {
428             _query.CopyTo(array, arrayIndex);
429         }
430
431         /// <summary>
432         /// Remove the given key value pair from the query
433         /// </summary>
434         /// <param name="item">The key value pair to remove</param>
435         /// <returns>True if operation is successful. Otherwise, false</returns>
436         /// <seealso cref="Add()"/>
437         /// <code>
438         /// ResourceQuery query = new ResourceQuery();
439         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
440         /// var result = query.Remove(new KeyValuePair<string, string>("key1", "value1"));
441         /// </code>
442         public bool Remove(KeyValuePair<string, string> item)
443         {
444             return Remove(item.Key);
445         }
446
447         /// <summary>
448         /// Get the enumerator to query collection
449         /// </summary>
450         /// <returns>Enumerator to query pairs</returns>
451         /// <code>
452         /// ResourceQuery query = new ResourceQuery();
453         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
454         /// query.Add(new KeyValuePair<string, string>("key2", "value2"));
455         /// foreach (KeyValuePair<string, string> pair in query)
456         /// {
457         ///     Console.WriteLine("key : {0}, value : {1}", pair.Key, pair.Value);
458         /// }
459         /// </code>
460         public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
461         {
462             return _query.GetEnumerator();
463         }
464
465         /// <summary>
466         /// Get the enumerator to query collection
467         /// </summary>
468         /// <returns>Enumerator to query pairs</returns>
469         /// <code>
470         /// ResourceQuery query = new ResourceQuery();
471         /// query.Add(new KeyValuePair<string, string>("key1", "value1"));
472         /// query.Add(new KeyValuePair<string, string>("key2", "value2"));
473         /// foreach (KeyValuePair<string, string> pair in query)
474         /// {
475         ///     Console.WriteLine("key : {0}, value : {1}", pair.Key, pair.Value);
476         /// }
477         /// </code>
478         IEnumerator IEnumerable.GetEnumerator()
479         {
480             return _query.GetEnumerator();
481         }
482
483         /// <summary>
484         /// Releases any unmanaged resources used by this object.
485         /// </summary>
486         public void Dispose()
487         {
488             Dispose(true);
489             GC.SuppressFinalize(this);
490         }
491
492         /// <summary>
493         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
494         /// </summary>
495         /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
496         protected virtual void Dispose(bool disposing)
497         {
498             if (_disposed)
499                 return;
500
501             if (disposing)
502             {
503                 // Free managed objects
504             }
505
506             Interop.IoTConnectivity.Common.Query.Destroy(_resourceQueryHandle);
507             _disposed = true;
508         }
509
510         private bool CanAddQuery(string newKey, string newValue)
511         {
512             int queryLenth = 0;
513             foreach (string key in Keys)
514             {
515                 queryLenth += key.Length + 2;
516             }
517             foreach (string value in Values)
518             {
519                 queryLenth += value.Length;
520             }
521
522             if ((newKey.Length + newValue.Length + queryLenth + 2) < QueryMaxLenth)
523                 return true;
524
525             return false;
526         }
527     }
528 }