2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Reflection;
19 using System.Collections.Generic;
21 using System.Runtime.InteropServices;
22 using Tizen.Internals.Errors;
24 namespace Tizen.Applications
27 /// A bundle object represents a bundle.
28 /// A bundle holds items (key-value pairs) and can be used with other Tizen APIs.
29 /// Keys can be used to access values.
30 /// This class is accessed by using a constructor to create a new instance of this object.
31 /// A bundle instance is not guaranteed to be thread safe if the instance is modified by multiple threads.
33 public class Bundle : IDisposable
35 private SafeBundleHandle _handle;
36 private bool _disposed = false;
37 private readonly HashSet<string> _keys;
40 /// The bundle constructor.
42 /// <exception cref="System.InvalidOperationException">Thrown when out of memory.</exception>
44 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
48 _handle = Interop.Bundle.Create();
49 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
50 _keys = new HashSet<string>();
54 /// The bundle constructor.
56 /// <param name="handle">The SafeBundleHandle instance.</param>
57 /// <exception cref="System.ArgumentNullException">Thrown when the handle is null or invalid.</exception>
58 public Bundle(SafeBundleHandle handle)
60 if (handle == null || handle.IsInvalid)
62 throw new ArgumentNullException("handle");
65 _handle = Interop.Bundle.DangerousClone(handle.DangerousGetHandle());
66 _keys = new HashSet<string>();
67 Interop.Bundle.Iterator iterator = (string key, int type, IntPtr keyval, IntPtr userData) =>
72 Interop.Bundle.Foreach(_handle, iterator, IntPtr.Zero);
73 if ((BundleErrorFactory.BundleError)ErrorFacts.GetLastResult() == BundleErrorFactory.BundleError.InvalidParameter)
75 throw new ArgumentException("Invalid parameter - cannot create bundle instance");
79 private enum BundleTypeProperty
86 private enum BundleType
90 String = 1 | BundleTypeProperty.Measurable,
91 StringArray = String | BundleTypeProperty.Array | BundleTypeProperty.Measurable,
93 ByteArray = Byte | BundleTypeProperty.Array
97 /// The number of items in a bundle object.
100 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
101 /// bundle.AddItem("string", "a_string");
102 /// Console.WriteLine("There are {0} items in the bundle", bundle.Count);
113 /// The keys in a bundle object.
116 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
117 /// bundle.AddItem("string1", "a_string1");
118 /// bundle.AddItem("string2", "a_string2");
119 /// bundle.AddItem("string3", "a_string3");
120 /// Console.WriteLine("The bundle contains the following keys:");
121 /// foreach(string key in bundle.Keys)
123 /// Console.WriteLine(key);
126 public IEnumerable<string> Keys
135 /// Gets the SafeBundleHandle instance.
137 public SafeBundleHandle SafeBundleHandle
139 get { return _handle; }
143 /// Releases any unmanaged resources used by this object.
145 public void Dispose()
148 GC.SuppressFinalize(this);
152 /// Checks whether the bundle contains an item with a specified key.
154 /// <param name="key">The key to check for.</param>
155 /// <returns>true if the bundle contains the key, false otherwise.</returns>
157 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
158 /// bundle.AddItem("string", "a_string");
159 /// if (bundle.Contains("string"))
161 /// string aValue = bundle.GetItem<string>("string");
162 /// Console.WriteLine(aValue);
165 public bool Contains(string key)
167 return _keys.Contains(key);
171 /// Adds an item into the bundle.
173 /// <param name="key">The key to identify the item with. If an item with the key already exists in the bundle, this method will not succeed.</param>
174 /// <param name="value">The value of the item.</param>
175 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
176 /// <exception cref="System.ArgumentNullException">Thrown when a value is null.</exception>
177 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
179 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
180 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
181 /// bundle.AddItem("byte_array", byteArray);
183 public void AddItem(string key, byte[] value)
187 throw new ArgumentNullException("value");
189 AddItem(key, value, 0, value.Length);
193 /// Adds an item into the bundle.
195 /// <param name="key">The key to identify the item with. If an item with the key already exists in the bundle, this method will not succeed.</param>
196 /// <param name="value">The value of the item.</param>
197 /// <param name="offset">The zero-based byte offset in value from which to add to the bundle.</param>
198 /// <param name="count">The maximum number of bytes to add to the bundle starting with offset.</param>
199 /// <exception cref="System.ArgumentOutOfRangeException">Thrown when the offset or count is out of range.</exception>
200 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
201 /// <exception cref="System.ArgumentNullException">Thrown when a value is null.</exception>
202 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
204 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
205 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
206 /// bundle.AddItem("byte_array", byteArray, 2, 3);
208 public void AddItem(string key, byte[] value, int offset, int count)
210 if (!_keys.Contains(key))
214 throw new ArgumentNullException("value");
218 throw new ArgumentOutOfRangeException("offset", offset, "Cannot be less than 0");
220 if (offset > value.Length - 1)
222 throw new ArgumentOutOfRangeException("offset", offset, "Greater than last index of array");
226 throw new ArgumentOutOfRangeException("count", count, "Must be at least 1");
228 if (offset + count > value.Length)
230 throw new ArgumentException("The count is too large for the specified offset");
232 // Code is in Interop file because it is unsafe
233 int ret = Interop.Bundle.UnsafeCode.AddItem(_handle, key, value, offset, count);
234 BundleErrorFactory.CheckAndThrowException(ret, _handle);
239 throw new ArgumentException("Key already exists", "key");
244 /// Adds an item into the bundle.
246 /// <param name="key">The key to identify the item with. If an item with the key already exists in the bundle, this method will not succeed.</param>
247 /// <param name="value">The value of the item.</param>
248 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
249 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
251 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
252 /// bundle.AddItem("string", "a_string");
254 public void AddItem(string key, string value)
256 if (!_keys.Contains(key))
258 int ret = Interop.Bundle.AddString(_handle, key, value);
259 BundleErrorFactory.CheckAndThrowException(ret, _handle);
264 throw new ArgumentException("Key already exists", "key");
269 /// Adds an item into the bundle.
271 /// <param name="key">The key to identify the item with. If an item with the key already exists in the bundle, this method will not succeed.</param>
272 /// <param name="value">The value of the item.</param>
273 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
274 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
276 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
277 /// string[] stringArray = { "a", "b", "c" };
278 /// bundle.AddItem("string_array", stringArray);
280 public void AddItem(string key, IEnumerable<string> value)
282 if (!_keys.Contains(key))
284 string[] valueArray = value.Select(v => v == null ? string.Empty : v).ToArray();
285 int ret = Interop.Bundle.AddStringArray(_handle, key, valueArray, valueArray.Count());
286 BundleErrorFactory.CheckAndThrowException(ret, _handle);
291 throw new ArgumentException("Key already exists", "key");
296 /// Gets the value of a bundle item with a specified key.
298 /// <param name="key">The key of the bundle item whose value is desired.</param>
299 /// <returns>The value of the bundle item.</returns>
300 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
301 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
303 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
304 /// bundle.AddItem("string", "a_string");
305 /// if (bundle.Contains("string"))
307 /// object aValue = bundle.GetItem("string");
308 /// if (bundle.Is<string>("string");)
310 /// string aString = (string)aValue;
311 /// Console.WriteLine(aString);
315 public object GetItem(string key)
317 if (_keys.Contains(key))
319 int type = Interop.Bundle.GetType(_handle, key);
320 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
323 case (int)BundleType.String:
326 int retString = Interop.Bundle.GetString(_handle, key, out stringPtr);
327 BundleErrorFactory.CheckAndThrowException(retString, _handle);
328 string stringValue = Marshal.PtrToStringAnsi(stringPtr);
329 if (stringValue == null)
333 case (int)BundleType.StringArray:
336 IntPtr stringArrayPtr = Interop.Bundle.GetStringArray(_handle, key, out stringArraySize);
337 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
338 string[] stringArray;
339 IntPtrToStringArray(stringArrayPtr, stringArraySize, out stringArray);
342 case (int)BundleType.Byte:
346 int retByte = Interop.Bundle.GetByte(_handle, key, out byteArrayPtr, out byteArraySize);
347 BundleErrorFactory.CheckAndThrowException(retByte, _handle);
348 byte[] byteArray = new byte[byteArraySize];
349 Marshal.Copy(byteArrayPtr, byteArray, 0, byteArraySize);
353 throw new ArgumentException("Key does not exist in the bundle", "key");
358 throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
363 /// Gets the value of a bundle item with a specified key.
364 /// Note that this is a generic method.
366 /// <typeparam name="T">The generic type to return.</typeparam>
367 /// <param name="key">The key of the bundle item whose value is desired.</param>
368 /// <returns>The value of the bundle item if it is of the specified generic type.</returns>
369 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
370 /// <exception cref="System.InvalidCastException">Thrown when the value of the bundle item cannot be converted to the specified generic type.</exception>
371 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
373 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
374 /// string[] stringArray = { "a", "b", "c" };
375 /// bundle.AddItem("string_array", stringArray);
376 /// if (bundle.Is<string>("string_array"))
378 /// Console.WriteLine("It is a string");
379 /// Console.WriteLine(bundle.GetItem<string>("string_array"));
381 /// else if (bundle.Is<string[]>("string_array"))
383 /// Console.WriteLine("It is a string[]");
384 /// string[] anArray = bundle.GetItem<string[]>("string_array");
385 /// foreach (string value in anArray)
387 /// Console.WriteLine(value);
391 public T GetItem<T>(string key)
393 return (T)GetItem(key);
397 /// Gets the value of a bundle item with a specified key.
399 /// <param name="key">The key of the bundle item whose value is desired.</param>
400 /// <param name="value">The value of the bundle item. If the key does not exist or the type of this parameter is incorrect, it is the default value for the value parameter type.</param>
401 /// <returns>true if an item with the key exists and if the value is the same type as the output value parameter, false otherwise.</returns>
402 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
404 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
405 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
406 /// bundle.AddItem("byte_array", byteArray);
407 /// byte[] aByteArray;
408 /// if (bundle.TryGetItem("byte_array", out aByteArray))
410 /// Console.WriteLine("First item in the byte array: {0}", aByteArray[0]);
413 public bool TryGetItem(string key, out byte[] value)
415 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.Byte)
417 value = GetItem<byte[]>(key);
422 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
424 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
426 value = default(byte[]);
432 /// Gets the value of a bundle item with a specified key.
434 /// <param name="key">The key of the bundle item whose value is desired.</param>
435 /// <param name="value">The value of the bundle item. If the key does not exist or the type of this parameter is incorrect, it is the default value for the value parameter type.</param>
436 /// <returns>true if an item with the key exists and if the value is the same type as the output value parameter, false otherwise.</returns>
437 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
439 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
440 /// bundle.AddItem("string", "a_string");
442 /// if (bundle.TryGetItem("string", out aString))
444 /// Console.WriteLine(aString);
447 public bool TryGetItem(string key, out string value)
449 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.String)
451 value = GetItem<string>(key);
456 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
458 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
460 value = default(string);
466 /// Gets the value of a bundle item with a specified key.
468 /// <param name="key">The key of the bundle item whose value is desired.</param>
469 /// <param name="value">The value of the bundle item. If the key does not exist or the type of this parameter is incorrect, it is the default value for the value parameter type.</param>
470 /// <returns>true if an item with the key exists and if the value is the same type as the output value parameter, false otherwise.</returns>
471 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
473 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
474 /// string[] stringArray = { "a", "b", "c" };
475 /// bundle.AddItem("string_array", stringArray);
476 /// System.Collections.Generic.IEnumerable<string> aStringEnumerable;
477 /// if (bundle.TryGetItem("string", out aStringEnumerable))
479 /// foreach (string value in aStringEnumerable)
481 /// Console.WriteLine(value);
485 public bool TryGetItem(string key, out IEnumerable<string> value)
487 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.StringArray)
489 value = GetItem<IEnumerable<string>>(key);
494 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
496 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
498 value = default(IEnumerable<string>);
504 /// Checks whether an item is of a specific type.
506 /// <typeparam name="T">The generic type to check for.</typeparam>
507 /// <param name="key">The key whose type wants to be checked.</param>
508 /// <returns>true if the item is of the specified type, false otherwise.</returns>
509 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
510 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
512 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
513 /// string[] stringArray = { "a", "b", "c" };
514 /// bundle.AddItem("string_array", stringArray);
515 /// if (bundle.Is<string[]>("string_array"))
517 /// Console.WriteLine("It is a string[]");
518 /// string[] anArray = bundle.GetItem<string[]>("string_array");
519 /// foreach (string value in anArray)
521 /// Console.WriteLine(value);
525 public bool Is<T>(string key)
527 if (_keys.Contains(key))
529 int type = Interop.Bundle.GetType(_handle, key);
532 case (int)BundleType.String:
533 return typeof(string) == typeof(T);
535 case (int)BundleType.StringArray:
536 return typeof(T).GetTypeInfo().IsAssignableFrom(typeof(string[]).GetTypeInfo());
538 case (int)BundleType.Byte:
539 return typeof(byte[]) == typeof(T);
542 throw new ArgumentException("Key does not exist in the bundle", "key");
547 throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
552 /// Removes a bundle item with a specific key from a Bundle.
554 /// <param name="key">The key of the item to delete.</param>
555 /// <returns>true if the item is successfully found and removed, false otherwise (even if the item is not found).</returns>
556 /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
557 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
559 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
560 /// bundle.AddItem("string", "a_string");
561 /// if (bundle.Contains("string"))
563 /// if (bundle.RemoveItem("string"))
565 /// Console.WriteLine("Removed");
569 public bool RemoveItem(string key)
571 if (_keys.Contains(key))
573 int ret = Interop.Bundle.RemoveItem(_handle, key);
574 if (ret == (int)BundleErrorFactory.BundleError.KeyNotAvailable)
578 BundleErrorFactory.CheckAndThrowException(ret, _handle);
589 /// Decodes an encoded bundle data.
591 /// <param name="bundleRaw">The encoded bundle data. bundleRaw should be the returned value of Tizen.Applications.Bundle.Encode, otherwise this method will not succeed.</param>
592 /// <returns>Decoded Bundle object.</returns>
593 /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
595 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
596 /// string bundleRaw = bundle.Encode();
597 /// Bundle data = bundle.Decode(bundleRaw);
599 public static Bundle Decode(string bundleRaw)
601 SafeBundleHandle handle;
603 handle = Interop.Bundle.BundleDecode(bundleRaw, bundleRaw.Length);
604 if (ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
606 throw new ArgumentException("Invalid bundle raw");
609 return new Bundle(handle);
613 /// Encodes bundle to string.
615 /// <returns>Encoded bundle data in string.</returns>
616 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
618 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
619 /// string bundleRaw = bundle.Encode();
621 public string Encode()
626 Interop.Bundle.BundleEncode(_handle, out bundleRaw, out len);
627 if (ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
629 throw new InvalidOperationException("Invalid bundle");
636 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
638 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
639 protected virtual void Dispose(bool disposing)
645 if (_handle != null && !_handle.IsInvalid)
654 /// Destructor of the bundle class.
661 static private void IntPtrToStringArray(IntPtr unmanagedArray, int size, out string[] managedArray)
663 managedArray = new string[size];
664 IntPtr[] IntPtrArray = new IntPtr[size];
666 Marshal.Copy(unmanagedArray, IntPtrArray, 0, size);
668 for (int iterator = 0; iterator < size; iterator++)
670 managedArray[iterator] = Marshal.PtrToStringAnsi(IntPtrArray[iterator]);
675 internal static class BundleErrorFactory
677 internal enum BundleError
679 None = ErrorCode.None,
680 OutOfMemory = ErrorCode.OutOfMemory,
681 InvalidParameter = ErrorCode.InvalidParameter,
682 KeyNotAvailable = ErrorCode.KeyNotAvailable,
683 KeyExists = -0x01180000 | 0x01
686 static internal void CheckAndThrowException(int error, SafeBundleHandle handle)
688 if ((BundleError)error == BundleError.None)
692 else if ((BundleError)error == BundleError.OutOfMemory)
694 throw new InvalidOperationException("Out of memory");
696 else if ((BundleError)error == BundleError.InvalidParameter)
698 if (handle.IsInvalid)
700 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
702 throw new ArgumentException("Invalid parameter");
704 else if ((BundleError)error == BundleError.KeyNotAvailable)
706 throw new ArgumentException("Key does not exist in the bundle");
708 else if ((BundleError)error == BundleError.KeyExists)
710 throw new ArgumentException("Key already exists");