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>
45 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
50 _handle = Interop.Bundle.Create();
51 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
52 _keys = new HashSet<string>();
56 /// The bundle constructor.
58 /// <param name="handle">The SafeBundleHandle instance.</param>
59 /// <exception cref="System.ArgumentNullException">Thrown when the handle is null or invalid.</exception>
60 public Bundle(SafeBundleHandle handle)
62 if (handle == null || handle.IsInvalid)
64 throw new ArgumentNullException("handle");
67 _handle = Interop.Bundle.DangerousClone(handle.DangerousGetHandle());
68 _keys = new HashSet<string>();
69 Interop.Bundle.Iterator iterator = (string key, int type, IntPtr keyval, IntPtr userData) =>
74 Interop.Bundle.Foreach(_handle, iterator, IntPtr.Zero);
75 if ((BundleErrorFactory.BundleError)ErrorFacts.GetLastResult() == BundleErrorFactory.BundleError.InvalidParameter)
77 throw new ArgumentException("Invalid parameter - cannot create bundle instance");
81 private enum BundleTypeProperty
88 private enum BundleType
92 String = 1 | BundleTypeProperty.Measurable,
93 StringArray = String | BundleTypeProperty.Array | BundleTypeProperty.Measurable,
95 ByteArray = Byte | BundleTypeProperty.Array
99 /// The number of items in a bundle object.
103 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
104 /// bundle.AddItem("string", "a_string");
105 /// Console.WriteLine("There are {0} items in the bundle", bundle.Count);
117 /// The keys in a bundle object.
121 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
122 /// bundle.AddItem("string1", "a_string1");
123 /// bundle.AddItem("string2", "a_string2");
124 /// bundle.AddItem("string3", "a_string3");
125 /// Console.WriteLine("The bundle contains the following keys:");
126 /// foreach(string key in bundle.Keys)
128 /// Console.WriteLine(key);
132 public IEnumerable<string> Keys
141 /// Gets the SafeBundleHandle instance.
143 public SafeBundleHandle SafeBundleHandle
145 get { return _handle; }
149 /// Releases any unmanaged resources used by this object.
151 public void Dispose()
154 GC.SuppressFinalize(this);
158 /// Checks whether the bundle contains an item with a specified key.
160 /// <param name="key">The key to check for.</param>
161 /// <returns>true if the bundle contains the key, false otherwise.</returns>
164 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
165 /// bundle.AddItem("string", "a_string");
166 /// if (bundle.Contains("string"))
168 /// string aValue = bundle.GetItem<string>("string");
169 /// Console.WriteLine(aValue);
173 public bool Contains(string key)
175 return _keys.Contains(key);
179 /// Adds an item into the bundle.
181 /// <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>
182 /// <param name="value">The value of the item.</param>
183 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
184 /// <exception cref="System.ArgumentNullException">Thrown when a value is null.</exception>
185 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
188 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
189 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
190 /// bundle.AddItem("byte_array", byteArray);
193 public void AddItem(string key, byte[] value)
197 throw new ArgumentNullException("value");
199 AddItem(key, value, 0, value.Length);
203 /// Adds an item into the bundle.
205 /// <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>
206 /// <param name="value">The value of the item.</param>
207 /// <param name="offset">The zero-based byte offset in value from which to add to the bundle.</param>
208 /// <param name="count">The maximum number of bytes to add to the bundle starting with offset.</param>
209 /// <exception cref="System.ArgumentOutOfRangeException">Thrown when the offset or count is out of range.</exception>
210 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
211 /// <exception cref="System.ArgumentNullException">Thrown when a value is null.</exception>
212 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
215 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
216 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
217 /// bundle.AddItem("byte_array", byteArray, 2, 3);
220 public void AddItem(string key, byte[] value, int offset, int count)
222 if (!_keys.Contains(key))
226 throw new ArgumentNullException("value");
230 throw new ArgumentOutOfRangeException("offset", offset, "Cannot be less than 0");
232 if (offset > value.Length - 1)
234 throw new ArgumentOutOfRangeException("offset", offset, "Greater than last index of array");
238 throw new ArgumentOutOfRangeException("count", count, "Must be at least 1");
240 if (offset + count > value.Length)
242 throw new ArgumentException("The count is too large for the specified offset");
244 // Code is in Interop file because it is unsafe
245 int ret = Interop.Bundle.UnsafeCode.AddItem(_handle, key, value, offset, count);
246 BundleErrorFactory.CheckAndThrowException(ret, _handle);
251 throw new ArgumentException("Key already exists", "key");
256 /// Adds an item into the bundle.
258 /// <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>
259 /// <param name="value">The value of the item.</param>
260 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
261 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
264 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
265 /// bundle.AddItem("string", "a_string");
268 public void AddItem(string key, string value)
270 if (!_keys.Contains(key))
272 int ret = Interop.Bundle.AddString(_handle, key, value);
273 BundleErrorFactory.CheckAndThrowException(ret, _handle);
278 throw new ArgumentException("Key already exists", "key");
283 /// Adds an item into the bundle.
285 /// <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>
286 /// <param name="value">The value of the item.</param>
287 /// <exception cref="System.ArgumentException">Thrown when the key already exists or when there is an invalid parameter.</exception>
288 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
291 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
292 /// string[] stringArray = { "a", "b", "c" };
293 /// bundle.AddItem("string_array", stringArray);
296 public void AddItem(string key, IEnumerable<string> value)
298 if (!_keys.Contains(key))
300 string[] valueArray = value.Select(v => v == null ? string.Empty : v).ToArray();
301 int ret = Interop.Bundle.AddStringArray(_handle, key, valueArray, valueArray.Count());
302 BundleErrorFactory.CheckAndThrowException(ret, _handle);
307 throw new ArgumentException("Key already exists", "key");
312 /// Gets the value of a bundle item with a specified key.
314 /// <param name="key">The key of the bundle item whose value is desired.</param>
315 /// <returns>The value of the bundle item.</returns>
316 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
317 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
320 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
321 /// bundle.AddItem("string", "a_string");
322 /// if (bundle.Contains("string"))
324 /// object aValue = bundle.GetItem("string");
325 /// if (bundle.Is<string>("string");)
327 /// string aString = (string)aValue;
328 /// Console.WriteLine(aString);
333 public object GetItem(string key)
335 if (_keys.Contains(key))
337 int type = Interop.Bundle.GetType(_handle, key);
338 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
341 case (int)BundleType.String:
344 int retString = Interop.Bundle.GetString(_handle, key, out stringPtr);
345 BundleErrorFactory.CheckAndThrowException(retString, _handle);
346 string stringValue = Marshal.PtrToStringAnsi(stringPtr);
347 if (stringValue == null)
351 case (int)BundleType.StringArray:
354 IntPtr stringArrayPtr = Interop.Bundle.GetStringArray(_handle, key, out stringArraySize);
355 BundleErrorFactory.CheckAndThrowException(ErrorFacts.GetLastResult(), _handle);
356 string[] stringArray;
357 IntPtrToStringArray(stringArrayPtr, stringArraySize, out stringArray);
360 case (int)BundleType.Byte:
364 int retByte = Interop.Bundle.GetByte(_handle, key, out byteArrayPtr, out byteArraySize);
365 BundleErrorFactory.CheckAndThrowException(retByte, _handle);
366 byte[] byteArray = new byte[byteArraySize];
367 Marshal.Copy(byteArrayPtr, byteArray, 0, byteArraySize);
371 throw new ArgumentException("Key does not exist in the bundle", "key");
376 throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
381 /// Gets the value of a bundle item with a specified key.
382 /// Note that this is a generic method.
384 /// <typeparam name="T">The generic type to return.</typeparam>
385 /// <param name="key">The key of the bundle item whose value is desired.</param>
386 /// <returns>The value of the bundle item if it is of the specified generic type.</returns>
387 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
388 /// <exception cref="System.InvalidCastException">Thrown when the value of the bundle item cannot be converted to the specified generic type.</exception>
389 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
392 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
393 /// string[] stringArray = { "a", "b", "c" };
394 /// bundle.AddItem("string_array", stringArray);
395 /// if (bundle.Is<string>("string_array"))
397 /// Console.WriteLine("It is a string");
398 /// Console.WriteLine(bundle.GetItem<string>("string_array"));
400 /// else if (bundle.Is<string[]>("string_array"))
402 /// Console.WriteLine("It is a string[]");
403 /// string[] anArray = bundle.GetItem<string[]>("string_array");
404 /// foreach (string value in anArray)
406 /// Console.WriteLine(value);
411 public T GetItem<T>(string key)
413 return (T)GetItem(key);
417 /// Gets the value of a bundle item with a specified key.
419 /// <param name="key">The key of the bundle item whose value is desired.</param>
420 /// <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>
421 /// <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>
422 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
425 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
426 /// byte[] byteArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
427 /// bundle.AddItem("byte_array", byteArray);
428 /// byte[] aByteArray;
429 /// if (bundle.TryGetItem("byte_array", out aByteArray))
431 /// Console.WriteLine("First item in the byte array: {0}", aByteArray[0]);
435 public bool TryGetItem(string key, out byte[] value)
437 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.Byte)
439 value = GetItem<byte[]>(key);
444 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
446 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
448 value = default(byte[]);
454 /// Gets the value of a bundle item with a specified key.
456 /// <param name="key">The key of the bundle item whose value is desired.</param>
457 /// <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>
458 /// <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>
459 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
462 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
463 /// bundle.AddItem("string", "a_string");
465 /// if (bundle.TryGetItem("string", out aString))
467 /// Console.WriteLine(aString);
471 public bool TryGetItem(string key, out string value)
473 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.String)
475 value = GetItem<string>(key);
480 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
482 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
484 value = default(string);
490 /// Gets the value of a bundle item with a specified key.
492 /// <param name="key">The key of the bundle item whose value is desired.</param>
493 /// <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>
494 /// <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>
495 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
498 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
499 /// string[] stringArray = { "a", "b", "c" };
500 /// bundle.AddItem("string_array", stringArray);
501 /// System.Collections.Generic.IEnumerable<string> aStringEnumerable;
502 /// if (bundle.TryGetItem("string", out aStringEnumerable))
504 /// foreach (string value in aStringEnumerable)
506 /// Console.WriteLine(value);
511 public bool TryGetItem(string key, out IEnumerable<string> value)
513 if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.StringArray)
515 value = GetItem<IEnumerable<string>>(key);
520 if (_keys.Contains(key) && ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
522 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
524 value = default(IEnumerable<string>);
530 /// Checks whether an item is of a specific type.
532 /// <typeparam name="T">The generic type to check for.</typeparam>
533 /// <param name="key">The key whose type wants to be checked.</param>
534 /// <returns>true if the item is of the specified type, false otherwise.</returns>
535 /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
536 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
539 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
540 /// string[] stringArray = { "a", "b", "c" };
541 /// bundle.AddItem("string_array", stringArray);
542 /// if (bundle.Is<string[]>("string_array"))
544 /// Console.WriteLine("It is a string[]");
545 /// string[] anArray = bundle.GetItem<string[]>("string_array");
546 /// foreach (string value in anArray)
548 /// Console.WriteLine(value);
553 public bool Is<T>(string key)
555 if (_keys.Contains(key))
557 int type = Interop.Bundle.GetType(_handle, key);
560 case (int)BundleType.String:
561 return typeof(string) == typeof(T);
563 case (int)BundleType.StringArray:
564 return typeof(T).GetTypeInfo().IsAssignableFrom(typeof(string[]).GetTypeInfo());
566 case (int)BundleType.Byte:
567 return typeof(byte[]) == typeof(T);
570 throw new ArgumentException("Key does not exist in the bundle", "key");
575 throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
580 /// Removes a bundle item with a specific key from a Bundle.
582 /// <param name="key">The key of the item to delete.</param>
583 /// <returns>true if the item is successfully found and removed, false otherwise (even if the item is not found).</returns>
584 /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
585 /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
588 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
589 /// bundle.AddItem("string", "a_string");
590 /// if (bundle.Contains("string"))
592 /// if (bundle.RemoveItem("string"))
594 /// Console.WriteLine("Removed");
599 public bool RemoveItem(string key)
601 if (_keys.Contains(key))
603 int ret = Interop.Bundle.RemoveItem(_handle, key);
604 if (ret == (int)BundleErrorFactory.BundleError.KeyNotAvailable)
608 BundleErrorFactory.CheckAndThrowException(ret, _handle);
619 /// Decodes an encoded bundle data.
621 /// <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>
622 /// <returns>Decoded Bundle object.</returns>
623 /// <exception cref="System.ArgumentException">Thrown when there is an invalid parameter.</exception>
626 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
627 /// string bundleRaw = bundle.Encode();
628 /// Bundle data = bundle.Decode(bundleRaw);
631 public static Bundle Decode(string bundleRaw)
633 SafeBundleHandle handle;
635 handle = Interop.Bundle.BundleDecode(bundleRaw, bundleRaw.Length);
636 if (ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
638 throw new ArgumentException("Invalid bundle raw");
641 return new Bundle(handle);
645 /// Encodes bundle to string.
647 /// <returns>Encoded bundle data in string.</returns>
648 /// <exception cref="System.InvalidOperationException">Thrown when out of memory or when the bundle instance has been disposed.</exception>
651 /// Tizen.Applications.Bundle bundle = new Tizen.Applications.Bundle();
652 /// string bundleRaw = bundle.Encode();
655 public string Encode()
660 Interop.Bundle.BundleEncode(_handle, out bundleRaw, out len);
661 if (ErrorFacts.GetLastResult() == (int)BundleErrorFactory.BundleError.InvalidParameter)
663 throw new InvalidOperationException("Invalid bundle");
670 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
672 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
673 protected virtual void Dispose(bool disposing)
679 if (_handle != null && !_handle.IsInvalid)
688 /// Destructor of the bundle class.
695 static private void IntPtrToStringArray(IntPtr unmanagedArray, int size, out string[] managedArray)
697 managedArray = new string[size];
698 IntPtr[] IntPtrArray = new IntPtr[size];
700 Marshal.Copy(unmanagedArray, IntPtrArray, 0, size);
702 for (int iterator = 0; iterator < size; iterator++)
704 managedArray[iterator] = Marshal.PtrToStringAnsi(IntPtrArray[iterator]);
709 internal static class BundleErrorFactory
711 internal enum BundleError
713 None = ErrorCode.None,
714 OutOfMemory = ErrorCode.OutOfMemory,
715 InvalidParameter = ErrorCode.InvalidParameter,
716 KeyNotAvailable = ErrorCode.KeyNotAvailable,
717 KeyExists = -0x01180000 | 0x01
720 static internal void CheckAndThrowException(int error, SafeBundleHandle handle)
722 if ((BundleError)error == BundleError.None)
726 else if ((BundleError)error == BundleError.OutOfMemory)
728 throw new InvalidOperationException("Out of memory");
730 else if ((BundleError)error == BundleError.InvalidParameter)
732 if (handle.IsInvalid)
734 throw new InvalidOperationException("Invalid bundle instance (object may have been disposed or released)");
736 throw new ArgumentException("Invalid parameter");
738 else if ((BundleError)error == BundleError.KeyNotAvailable)
740 throw new ArgumentException("Key does not exist in the bundle");
742 else if ((BundleError)error == BundleError.KeyExists)
744 throw new ArgumentException("Key already exists");