[Bundle] Add GetItem<T>(), TryGetItem(), Is<T>(), Contains()
authorSidharth Gupta <sid92.gupta@samsung.com>
Thu, 10 Mar 2016 07:00:30 +0000 (16:00 +0900)
committerSidharth Gupta <sid92.gupta@samsung.com>
Tue, 22 Mar 2016 08:55:45 +0000 (17:55 +0900)
- Added internal constructor for other modules to use.
- Common Tizen errors have been added to the Tizen.Internals
  namespace. Hence, these errors are now checked for here.
- Common Tizen errors have been equated to Bundle errors in a
  private enum.

Signed-off-by: Sidharth Gupta <sid92.gupta@samsung.com>
Change-Id: Icb91b3590825dd0c6900a128b7c75f0e6faf120e

Tizen.Applications/Interop/Interop.Bundle.cs [changed mode: 0755->0644]
Tizen.Applications/Tizen.Applications.csproj [changed mode: 0755->0644]
Tizen.Applications/Tizen.Applications/Bundle.cs [changed mode: 0755->0644]
packaging/csapi-application.spec
src/Makefile [new file with mode: 0644]

old mode 100755 (executable)
new mode 100644 (file)
index eccd3c8..6d84fa5
@@ -14,6 +14,9 @@ internal static partial class Interop
 {
     internal static partial class Bundle
     {
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void Iterator(string key, int type, IntPtr keyval, IntPtr userData);
+
         [DllImport(Libraries.Bundle, EntryPoint = "bundle_create", CallingConvention = CallingConvention.Cdecl)]
         internal static extern IntPtr Create();
 
@@ -24,7 +27,7 @@ internal static partial class Interop
         internal static extern int Count(IntPtr handle);
 
         [DllImport(Libraries.Bundle, EntryPoint = "bundle_del", CallingConvention = CallingConvention.Cdecl)]
-        internal static extern int DeleteItem(IntPtr handle, string key);
+        internal static extern int RemoveItem(IntPtr handle, string key);
 
         [DllImport(Libraries.Bundle, EntryPoint = "bundle_add_str", CallingConvention = CallingConvention.Cdecl)]
         internal static extern int AddString(IntPtr handle, string key, string value);
@@ -47,13 +50,16 @@ internal static partial class Interop
         [DllImport(Libraries.Bundle, EntryPoint = "bundle_get_str_array", CallingConvention = CallingConvention.Cdecl)]
         internal static extern IntPtr GetStringArray(IntPtr handle, string key, out int size);
 
+        [DllImport(Libraries.Bundle, EntryPoint = "bundle_foreach", CallingConvention = CallingConvention.Cdecl)]
+        internal static extern void Foreach(IntPtr handle, Iterator iterator, IntPtr userData);
+
         internal static class UnsafeCode
         {
-            internal static unsafe void AddItem(IntPtr handle, string key, byte[] value, int offset, int count)
+            internal static unsafe int AddItem(IntPtr handle, string key, byte[] value, int offset, int count)
             {
                 fixed (byte* pointer = value)
                 {
-                    AddByte(handle, key, pointer + offset, count);
+                    return AddByte(handle, key, pointer + offset, count);
                 }
             }
         }
old mode 100755 (executable)
new mode 100644 (file)
index 8e471b3..ffb1e57
@@ -46,6 +46,9 @@
     <Reference Include="System.Data" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Xml" />
+    <Reference Include="Tizen.Internals">
+      <HintPath>..\..\tizen\Tizen.Internals\bin\Debug\Tizen.Internals.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Interop\Interop.AppControl.cs" />
old mode 100755 (executable)
new mode 100644 (file)
index f8d7dea..b47d5c1
@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.InteropServices;
@@ -13,17 +13,49 @@ namespace Tizen.Applications
     /// </summary>
     public class Bundle : IDisposable
     {
-        internal IntPtr _handle;
+        private IntPtr _handle;
         private bool _disposed = false;
-        private readonly List<string> _keys;
+        private readonly HashSet<string> _keys;
 
         /// <summary>
         /// The Bundle constructor.
         /// </summary>
         public Bundle()
         {
-            _keys = new List<string>();
             _handle = Interop.Bundle.Create();
+            if ((BundleError)Internals.Errors.ErrorFacts.GetLastResult() == BundleError.OutOfMemory)
+            {
+                throw new InvalidOperationException("Out of memory");
+            }
+            _keys = new HashSet<string>();
+        }
+
+        internal Bundle(IntPtr handle)
+        {
+            if (handle != IntPtr.Zero)
+            {
+                _handle = handle;
+                _keys = new HashSet<string>();
+                Interop.Bundle.Iterator iterator = (string key, int type, IntPtr keyval, IntPtr userData) =>
+                {
+                    _keys.Add(key);
+                };
+
+                Interop.Bundle.Foreach(_handle, iterator, IntPtr.Zero);
+            }
+            else
+            {
+                throw new ArgumentNullException("Invalid bundle");
+            }
+        }
+
+        private enum BundleError
+        {
+            None = Internals.Errors.ErrorCode.None,
+            OutOfMemory = Internals.Errors.ErrorCode.OutOfMemory,
+            InvalidParameter = Internals.Errors.ErrorCode.InvalidParameter,
+            KeyNotAvailable = Internals.Errors.ErrorCode.KeyNotAvailable,
+            KeyExists = -0x01180000 | 0x01
         }
 
         private enum BundleTypeProperty
@@ -38,9 +70,9 @@ namespace Tizen.Applications
             None = -1,
             Any = 0,
             String = 1 | BundleTypeProperty.Measurable,
-            StringArray = BundleType.String | BundleTypeProperty.Array | BundleTypeProperty.Measurable,
+            StringArray = String | BundleTypeProperty.Array | BundleTypeProperty.Measurable,
             Byte = 2,
-            ByteArray = BundleType.Byte | BundleTypeProperty.Array
+            ByteArray = Byte | BundleTypeProperty.Array
         }
 
         /// <summary>
@@ -64,7 +96,7 @@ namespace Tizen.Applications
                 return _keys;
             }
         }
-        
+
         /// <summary>
         /// Releases any unmanaged resources used by this object.
         /// </summary>
@@ -75,6 +107,16 @@ namespace Tizen.Applications
         }
 
         /// <summary>
+        /// Checks whether the bundle contains an item with a specified key.
+        /// </summary>
+        /// <param name="key">The key to check for.</param>
+        /// <returns>true if the bundle contains the key. false otherwise.</returns>
+        public bool Contains(string key)
+        {
+            return _keys.Contains(key);
+        }
+
+        /// <summary>
         /// Adds an item into the bundle.
         /// </summary>
         /// <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>
@@ -95,14 +137,37 @@ namespace Tizen.Applications
         {
             if (!_keys.Contains(key))
             {
-                // TODO: validate offset and count
+                if (offset < 0)
+                {
+                    throw new ArgumentOutOfRangeException("offset", offset, "Cannot be less than 0");
+                }
+                if (offset > value.Length - 1)
+                {
+                    throw new ArgumentOutOfRangeException("offset", offset, "Greater than last index of array");
+                }
+                if (count < 1)
+                {
+                    throw new ArgumentOutOfRangeException("count", count, "Must be at least 1");
+                }
+                if (offset + count > value.Length)
+                {
+                    throw new ArgumentException("The count is too large for the specified offset");
+                }
                 // Code is in Interop file because it is unsafe
-                Interop.Bundle.UnsafeCode.AddItem(_handle, key, value, offset, count);
+                int ret = Interop.Bundle.UnsafeCode.AddItem(_handle, key, value, offset, count);
+                if ((BundleError)ret == BundleError.InvalidParameter)
+                {
+                    throw new ArgumentException("Invalid parameter (key may be null or empty string)");
+                }
+                if ((BundleError)ret == BundleError.OutOfMemory)
+                {
+                    throw new InvalidOperationException("Out of memory");
+                }
                 _keys.Add(key);
             }
             else
             {
-                // TODO: handle when key already exists
+                throw new ArgumentException("Key already exists", "key");
             }
         }
 
@@ -115,12 +180,20 @@ namespace Tizen.Applications
         {
             if (!_keys.Contains(key))
             {
-                Interop.Bundle.AddString(_handle, key, value);
+                int ret = Interop.Bundle.AddString(_handle, key, value);
+                if ((BundleError)ret == BundleError.InvalidParameter)
+                {
+                    throw new ArgumentException("Invalid parameter (key may be null or empty string)");
+                }
+                if ((BundleError)ret == BundleError.OutOfMemory)
+                {
+                    throw new InvalidOperationException("Out of memory");
+                }
                 _keys.Add(key);
             }
             else
             {
-                // TODO: handle when key already exists
+                throw new ArgumentException("Key already exists", "key");
             }
         }
 
@@ -134,12 +207,20 @@ namespace Tizen.Applications
             if (!_keys.Contains(key))
             {
                 string[] valueArray = value.ToArray();
-                Interop.Bundle.AddStringArray(_handle, key, valueArray, valueArray.Count());
+                int ret = Interop.Bundle.AddStringArray(_handle, key, valueArray, valueArray.Count());
+                if ((BundleError)ret == BundleError.InvalidParameter)
+                {
+                    throw new ArgumentException("Invalid parameter (key may be null or empty string)");
+                }
+                if ((BundleError)ret == BundleError.OutOfMemory)
+                {
+                    throw new InvalidOperationException("Out of memory");
+                }
                 _keys.Add(key);
             }
             else
             {
-                // TODO: handle when key already exists
+                throw new ArgumentException("Key already exists", "key");
             }
         }
 
@@ -159,20 +240,17 @@ namespace Tizen.Applications
                     // get string
                     IntPtr stringPtr;
                     Interop.Bundle.GetString(_handle, key, out stringPtr);
-                    string stringResult = Marshal.PtrToStringAuto(stringPtr);
-                    return stringResult;
+                    return Marshal.PtrToStringAuto(stringPtr);
 
                     case (int)BundleType.StringArray:
                     // get string array
-                    IntPtr stringArrayPtr;
                     int stringArraySize;
-                    stringArrayPtr = Interop.Bundle.GetStringArray(_handle, key, out stringArraySize);
+                    IntPtr stringArrayPtr = Interop.Bundle.GetStringArray(_handle, key, out stringArraySize);
                     string[] stringArray;
                     IntPtrToStringArray(stringArrayPtr, stringArraySize, out stringArray);
                     return stringArray;
 
                     case (int)BundleType.Byte:
-                    case (int)BundleType.ByteArray:
                     // get byte array
                     IntPtr byteArrayPtr;
                     int byteArraySize;
@@ -182,12 +260,116 @@ namespace Tizen.Applications
                     return byteArray;
 
                     default:
-                    return "PROBLEM"; // TODO: Handle this
+                    throw new ArgumentException("Key does not exist in the bundle", "key");
+                }
+            }
+            else
+            {
+                throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of a bundle item with a specified key.
+        /// Note that this is a generic method.
+        /// </summary>
+        /// <typeparam name="T">The generic type to return.</typeparam>
+        /// <param name="key">The key of the bundle item whose value is desired.</param>
+        /// <returns>The value of the bundle item if it is of the specified generic type.</returns>
+        public T GetItem<T>(string key)
+        {
+            return (T)GetItem(key);
+        }
+
+        /// <summary>
+        /// Gets the value of a bundle item with a specified key.
+        /// </summary>
+        /// <param name="key">The key of the bundle item whose value is desired.</param>
+        /// <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>
+        /// <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>
+        public bool TryGetItem(string key, out byte[] value)
+        {
+            if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.Byte)
+            {
+                value = GetItem<byte[]>(key);
+                return true;
+            }
+            else
+            {
+                value = default(byte[]);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of a bundle item with a specified key.
+        /// </summary>
+        /// <param name="key">The key of the bundle item whose value is desired.</param>
+        /// <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>
+        /// <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>
+        public bool TryGetItem(string key, out string value)
+        {
+            if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.String)
+            {
+                value = GetItem<string>(key);
+                return true;
+            }
+            else
+            {
+                value = default(string);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of a bundle item with a specified key.
+        /// </summary>
+        /// <param name="key">The key of the bundle item whose value is desired.</param>
+        /// <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>
+        /// <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>
+        public bool TryGetItem(string key, out IEnumerable<string> value)
+        {
+            if (_keys.Contains(key) && Interop.Bundle.GetType(_handle, key) == (int)BundleType.StringArray)
+            {
+                value = GetItem<IEnumerable<string>>(key);
+                return true;
+            }
+            else
+            {
+                value = default(IEnumerable<string>);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Checks whether an item is of a specific type.
+        /// </summary>
+        /// <typeparam name="T">The generic type to check for.</typeparam>
+        /// <param name="key">The key whose type wants to be checked.</param>
+        /// <returns>true if the item is of the specified type. false otherwise.</returns>
+        public bool Is<T>(string key)
+        {
+            if (_keys.Contains(key))
+            {
+                int type = Interop.Bundle.GetType(_handle, key);
+                switch (type)
+                {
+                    case (int)BundleType.String:
+                    return typeof(string) == typeof(T);
+
+                    case (int)BundleType.StringArray:
+                    return typeof(T).IsAssignableFrom(typeof(string[]));
+
+                    case (int)BundleType.Byte:
+                    return typeof(byte[]) == typeof(T);
+
+                    default:
+                    throw new ArgumentException("Key does not exist in the bundle", "key");
                 }
             }
             else
             {
-                return "PROBLEM"; // TODO: handle when key does not exist
+                throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
             }
         }
 
@@ -199,19 +381,19 @@ namespace Tizen.Applications
         {
             if (_keys.Contains(key))
             {
-                Interop.Bundle.DeleteItem(_handle, key);
+                Interop.Bundle.RemoveItem(_handle, key);
                 _keys.Remove(key);
             }
             else
             {
-                // TODO: handle when key does not exist
+                throw new ArgumentException("Key does not exist in the bundle (may be null or empty string)", "key");
             }
         }
 
         /// <summary>
-        /// 
+        /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
         /// </summary>
-        /// <param name="disposing"></param>
+        /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
         protected virtual void Dispose(bool disposing)
         {
             if (!_disposed)
index 65996ca..0f5f59d 100644 (file)
@@ -14,26 +14,18 @@ Source0:    %{name}-%{version}.tar.gz
 Source1:    %{name}.manifest
 Source2:    %{name}.pc.in
 
-# TODO: replace mono-compiler, mono-devel to mcs, mono-shlib-cop
 BuildRequires: mono-compiler
 BuildRequires: mono-devel
-# TODO: replace mono-core to gacutil.
-#       mono-core should provide the symbol 'gacutil'
-Requires(post): mono-core
-Requires(postun): mono-core
-
-# P/Invoke Dependencies
+BuildRequires: pkgconfig(csapi-tizen)
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(capi-appfw-application)
-BuildRequires: pkgconfig(evas)
 
-# P/Invoke Runtime Dependencies
-# TODO: It should be removed after fix tizen-rpm-config
 Requires: glib-2.0
 Requires: capi-appfw-application
 Requires: evas
-# DLL Dependencies
-#BuildRequires: ...
+
+Requires(post): mono-core
+Requires(postun): mono-core
 
 %description
 Tizen API for C#
@@ -52,19 +44,7 @@ Development package for %{name}
 cp %{SOURCE1} .
 
 %build
-# build dll
-mcs -target:library -out:%{dllname} -keyfile:Tizen.Applications/Tizen.Applications.snk \
-  Tizen.Applications/Properties/AssemblyInfo.cs \
-  Tizen.Applications/Tizen.Applications/*.cs \
-  Tizen.Applications/Tizen.UI/*.cs \
-  Tizen.Applications/Interop/*.cs -unsafe
-
-# check p/invoke
-if [ -x %{dllname} ]; then
-  RET=`mono-shlib-cop %{dllname}`; \
-  CNT=`echo $RET | grep -E "^error:" | wc -l`; \
-  if [ $CNT -gt 0 ]; then exit 1; fi
-fi
+make
 
 %install
 # copy dll
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..534bc68
--- /dev/null
@@ -0,0 +1,24 @@
+ASM_DIRS := Tizen.Applications
+ASM_DLLS := $(addsuffix .dll,$(ASM_DIRS))
+
+FLAGS := /unsafe
+
+ALL: $(ASM_DLLS)
+
+define make-dll
+$(eval ASM = $(strip $1))
+$(eval SRC = $(shell find $(ASM) -path $(ASM)/obj -prune -o -name '*.cs' -print))
+$(eval PKG = $(shell echo $2 | tr ' ' ','))
+$(ASM).dll: $(SRC)
+       @echo "[BUILD] $$@"
+       @mcs /nologo /out:$$@ /t:library /keyfile:$(ASM)/$(ASM).snk $(addprefix /pkg:,$(PKG)) $(FLAGS) $(SRC)
+       @echo "[CHECK] $$@"
+       @RET=`mono-shlib-cop $$@`; \
+  CNT=`echo $$$$RET | grep -e '^error:' | wc -l`; \
+  if [ $$$$CNT -gt 0 ]; then echo $$$$RET; rm -f $$@ exit 1; fi
+endef
+
+$(eval $(call make-dll, Tizen.Applications, csapi-tizen))
+
+clean:
+       @rm -f $(ASM_DLLS)