Added json serialization of permissions in PermissionConverter.cs
authorPanagiotis Kanavos <pkanavos@gmail.com>
Wed, 26 Oct 2011 20:11:24 +0000 (23:11 +0300)
committerPanagiotis Kanavos <pkanavos@gmail.com>
Wed, 26 Oct 2011 20:11:24 +0000 (23:11 +0300)
Added json serialization of Pithos version timestamp dates in PithosDateTimeConverter.cs
Replaced redundant ObjectInfo properties with Json named attributes

Build intentionally left broken, moving code to another machine.

Next step, create the File Properties window

trunk/Pithos.Client.WPF/FileProperties/FilePropertiesViewModel.cs
trunk/Pithos.Core/Pithos.Core.csproj
trunk/Pithos.Core/packages.config
trunk/Pithos.Interfaces/ObjectInfo.cs
trunk/Pithos.Interfaces/PermissionConverter.cs [new file with mode: 0644]
trunk/Pithos.Interfaces/Pithos.Interfaces.csproj
trunk/Pithos.Interfaces/PithosDateTimeConverter.cs [new file with mode: 0644]
trunk/Pithos.Network.Test/ObjectInfoTest.cs
trunk/Pithos.Network/TreeHash.cs

index 8ea08fe..e417282 100644 (file)
@@ -4,8 +4,11 @@
 // </copyright>
 // -----------------------------------------------------------------------
 
+using System.Collections.Concurrent;
+using System.Collections.ObjectModel;
 using System.ComponentModel.Composition;
 using Caliburn.Micro;
+using Pithos.Interfaces;
 
 namespace Pithos.Client.WPF
 {
@@ -20,9 +23,68 @@ namespace Pithos.Client.WPF
     [Export(typeof(FilePropertiesViewModel))]
     public class FilePropertiesViewModel : Screen, IShell
     {
-        public FilePropertiesViewModel()
+
+        public FilePropertiesViewModel(ShellViewModel shell,ObjectInfo pithosFile)
+        {
+            Shell = shell;
+            PithosFile = pithosFile;
+        }
+
+        protected ShellViewModel Shell { get; set; }
+
+        private ObjectInfo _pithosFile;
+        public ObjectInfo PithosFile
+        {
+            get { return _pithosFile; }
+            set
+            {
+                _pithosFile = value;
+                
+                NotifyOfPropertyChange(()=>PithosFile);
+            }
+        }
+
+        private ObservableConcurrentDictionary<string, string> _permissions;
+        public ObservableConcurrentDictionary<string, string> Permissions
+        {
+            get { return _permissions; }
+            set
+            {
+                _permissions = value;
+            }
+        }
+
+
+        public void Refresh()
+        {
+            PithosFile=Shell.RetrieveObjectInfo(PithosFile);
+        }
+
+        public override void CanClose(Action<bool> callback)
+        {
+            base.CanClose(callback);
+        }
+
+        public void SaveChanges()
+        {
+            DoSave();
+            TryClose(true);
+        }
+
+        public void RejectChanges()
+        {
+            TryClose(false);
+        }
+
+        public void ApplyChanges()
+        {
+            DoSave();
+        }
+
+        private void DoSave()
         {
             
         }
+
     }
 }
index 54de75b..0f453a5 100644 (file)
     <Reference Include="NHibernate.Search, Version=0.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
       <HintPath>..\Libraries\NHibernate.Search.dll</HintPath>
     </Reference>
+    <Reference Include="ServiceStack.Text">
+      <HintPath>..\packages\ServiceStack.Text.2.27\lib\net35\ServiceStack.Text.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.ComponentModel.Composition" />
     <Reference Include="System.Core" />
     <Compile Include="WorkflowState.cs" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\Libraries\Json40r2\Source\Src\Newtonsoft.Json\Newtonsoft.Json.csproj">
-      <Project>{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}</Project>
-      <Name>Newtonsoft.Json</Name>
-    </ProjectReference>
     <ProjectReference Include="..\Libraries\ParallelExtensionsExtras\ParallelExtensionsExtras.csproj">
       <Project>{C45218F8-09E7-4F57-85BC-5D8D2AC736A3}</Project>
       <Name>ParallelExtensionsExtras</Name>
index 6fcbff9..8a13a2d 100644 (file)
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="Windows7APICodePack" version="1.0.0.0" />
-  <package id="Castle.Core" version="2.5.2" />
-  <package id="Castle.Components.Validator" version="2.5.0" />
   <package id="Castle.Activerecord" version="3.0.0.1" />
+  <package id="Castle.Components.Validator" version="2.5.0" />
+  <package id="Castle.Core" version="2.5.2" />
   <package id="Iesi.Collections" version="3.1.0.4000" />
   <package id="NHibernate" version="3.1.0.4000" />
   <package id="NHibernate.Castle" version="3.1.0.4000" />
+  <package id="ServiceStack.Text" version="2.27" />
+  <package id="Windows7APICodePack" version="1.0.0.0" />
 </packages>
\ No newline at end of file
index 4c89d7e..0e6a3ff 100644 (file)
@@ -1,14 +1,17 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics.Contracts;
+using System.Dynamic;
 using System.Globalization;
 using System.IO;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json;
 
 namespace Pithos.Interfaces
 {
-    public class ObjectInfo
+    public class ObjectInfo:DynamicObject 
     {
-        private List<string> _knownContainers= new List<string>{"trash"};
+        private readonly List<string> _knownContainers= new List<string>{"trash"};
         public string Name { get; set; }
         public string Hash { get; set; }
         public long Bytes { get; set; }
@@ -32,85 +35,43 @@ namespace Pithos.Interfaces
                 ExtractKnownExtensions();
             }
         }
-
-        private long? _version;
-        public long? Version
-        {
-            get { return _version; }
-            set { _version = value; }
-        }
         
-        //Alias for version, for Json deserialization purposes
-        public long? X_Object_Version
+        
+        private Dictionary<string, string> _permissions=new Dictionary<string, string>();
+        [JsonProperty("x_object_sharing")]
+        [JsonConverter(typeof(PermissionConverter))]
+        public Dictionary<string, string> Permissions
         {
-            get { return _version; }
-            set { _version = value; }
+            get { return _permissions; }
+            set
+            {
+                _permissions = value;                
+            }
         }
 
-        private string _allowedTo;
-        public string AllowedTo
-        {
-            get { return _allowedTo; }
-            set { _allowedTo = value; }
-        }
+        /// <summary>
+        /// Version number
+        /// </summary>
+        [JsonProperty("x_object_version")]
+        public long? Version { get; set; }
 
-        //Object permissions for Json deserialization, can be Read or Write
-        
-        public string x_object_allowed_to
-        {
-            get { return _allowedTo; }
-            set { _allowedTo = value; }
-        }
 
-        //Object permissions for HEAD deserialization, can be Read or Write        
-        public string X_Object_Allowed_To
-        {
-            get { return _allowedTo; }
-            set { _allowedTo = value; }
-        }
+        /// <summary>
+        /// Shared object permissions can be Read or Write
+        /// </summary>
+        [JsonProperty("x_object_allowed_to")]
+        public string AllowedTo { get; set; }
 
-        //Alias for VersionTimestamp, for Json deserialization purposes
-        //The x_object_version_timestamp is a unix timestamp.
-        public double? X_Object_Version_Timestamp
-        {
-            get
-            {
-                if (_versionTimestamp.HasValue)
-                    return (_versionTimestamp.Value-_epoch).TotalSeconds;
-                return null;
-            }
-            set
-            {                
-                if (value.HasValue)
-                {
-                    _versionTimestamp = _epoch.AddSeconds(value.Value);
-                }
-                else
-                {
-                    _versionTimestamp = null;
-                }
-            }
-        }
 
-        private DateTime? _versionTimestamp;
-        public DateTime? VersionTimestamp
-        {
-            get { return _versionTimestamp; }
-            set { _versionTimestamp = value; }
-        }
+        /// <summary>
+        /// Version timestamp
+        /// </summary>
+        [JsonProperty("X_Object_Version_Timestamp"), JsonConverter(typeof(PithosDateTimeConverter))]
+        public DateTime? VersionTimestamp { get; set; }
 
-        public string ModifiedBy
-        {
-            get{ return _modifiedBy;  }
-            set{ _modifiedBy = value; }
-        }
+        [JsonProperty("X_Object_Modified_By")]
+        public string ModifiedBy { get; set; }
 
-        //Alias for ModifiedBy, for Json deserialization purposes
-        public string X_Object_Modified_By
-        {
-            get{ return _modifiedBy;  }
-            set{ _modifiedBy = value; }
-        }
 
         public Stream Stream { get; set; }
 
@@ -164,14 +125,9 @@ namespace Pithos.Interfaces
             Last_Modified = DateTime.MinValue
         };
 
-        private string _modifiedBy;
-        private DateTime _epoch = new DateTime(1970, 1, 1);
-
-
-
         public string RelativeUrlToFilePath(string currentAccount)
         {
-            if (this.Name==null)
+            if (Name==null)
                 throw new InvalidOperationException("Name can't be null");
             if (String.IsNullOrWhiteSpace(currentAccount))
                 throw new ArgumentNullException("currentAccount");
@@ -180,7 +136,7 @@ namespace Pithos.Interfaces
             if (this == Empty)
                 return String.Empty;
 
-            var unescaped = Uri.UnescapeDataString(this.Name);
+            var unescaped = Uri.UnescapeDataString(Name);
             var path = unescaped.Replace("/", "\\");
             var pathParts=new Stack<string>();
             pathParts.Push(path);
@@ -198,5 +154,14 @@ namespace Pithos.Interfaces
             return finalPath;
         }
 
+        public override bool TrySetMember(SetMemberBinder binder, object value)
+        {
+            if (binder.Name.StartsWith("x_object_meta"))
+            {
+                Tags[binder.Name] = value.ToString();
+            }
+            return false;
+        }
+
     }
 }
\ No newline at end of file
diff --git a/trunk/Pithos.Interfaces/PermissionConverter.cs b/trunk/Pithos.Interfaces/PermissionConverter.cs
new file mode 100644 (file)
index 0000000..c9c7219
--- /dev/null
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Newtonsoft.Json;
+
+namespace Pithos.Interfaces
+{
+    public class PermissionConverter:JsonConverter
+    {
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            var dict = (Dictionary<string, string>) value;
+            var pairs = from pair in dict
+                        group pair by pair.Value into g
+                        select new {Permission=g.Key,Names=String.Join(",", g.SelectMany(p=>p.Key))};
+
+            var result=pairs.Aggregate(new StringBuilder(),
+                                       (builder, perm) => 
+                                       builder.AppendFormat("{0}={1};", perm.Permission, perm.Names));
+
+            var finalValue = result.ToString();
+            writer.WriteValue(finalValue);
+        }
+
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            
+            var permissionString= (string) reader.Value;
+
+            var permissions = (from permisson in permissionString.Split(';')
+                               let parsed = permisson.Split('=')
+                               from account in parsed[1].Split(',')
+                               select new { account, Permission = parsed[0] })
+                .ToDictionary(perm=>perm.account,perm=>perm.Permission);
+
+            return permissions;
+        }
+
+        public override bool CanConvert(Type objectType)
+        {
+            return (objectType==typeof(Dictionary<string,string>));
+        }
+    }
+}
\ No newline at end of file
index 850771c..9c92c1e 100644 (file)
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Compile Include="AccountSettings.cs" />
     <Compile Include="IPithosSettings.cs" />
     <Compile Include="IStatusChecker.cs" />
+    <Compile Include="PermissionConverter.cs" />
+    <Compile Include="PithosDateTimeConverter.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="KnownExtensions.cs" />
     <Compile Include="ObjectInfo.cs" />
     <Compile Include="PithosSettingsData.cs" />
       <LastGenOutput>Settings.Designer.cs</LastGenOutput>
     </None>
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Libraries\Json40r2\Source\Src\Newtonsoft.Json\Newtonsoft.Json.csproj">
+      <Project>{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}</Project>
+      <Name>Newtonsoft.Json</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/trunk/Pithos.Interfaces/PithosDateTimeConverter.cs b/trunk/Pithos.Interfaces/PithosDateTimeConverter.cs
new file mode 100644 (file)
index 0000000..451fb7d
--- /dev/null
@@ -0,0 +1,91 @@
+using System;
+using System.Globalization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json.Utilities;
+
+namespace Pithos.Interfaces
+{
+    /// <summary>
+    /// Converts a <see cref="DateTime"/> to and from a JavaScript date constructor (e.g. new Date(52231943)).
+    /// </summary>
+    public class PithosDateTimeConverter : DateTimeConverterBase
+    {
+        private DateTime _epoch = new DateTime(1970, 1, 1,0,0,0,DateTimeKind.Utc);
+
+        /// <summary>
+        /// Writes the JSON representation of the object.
+        /// </summary>
+        /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
+        /// <param name="value">The value.</param>
+        /// <param name="serializer">The calling serializer.</param>
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            double seconds;
+
+            if (value is DateTime)
+            {
+                DateTime dateTime = (DateTime)value;
+                DateTime utcDateTime = dateTime.ToUniversalTime();
+                seconds = (utcDateTime - _epoch).TotalSeconds; ;
+            }
+#if !PocketPC && !NET20
+            else if (value is DateTimeOffset)
+            {
+                DateTimeOffset dateTimeOffset = (DateTimeOffset)value;
+                DateTimeOffset utcDateTimeOffset = dateTimeOffset.ToUniversalTime();
+                seconds= (utcDateTimeOffset.UtcDateTime - _epoch).TotalSeconds;
+            }
+#endif
+            else
+            {
+                throw new Exception("Expected date object value.");
+            }
+
+            writer.WriteValue(seconds);
+        }
+
+        /// <summary>
+        /// Reads the JSON representation of the object.
+        /// </summary>
+        /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
+        /// <param name="objectType">Type of the object.</param>
+        /// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
+        /// <param name="serializer">The calling serializer.</param>
+        /// <returns>The object value.</returns>
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            Type t = (IsNullableType(objectType))
+                         ? Nullable.GetUnderlyingType(objectType)
+                         : objectType;
+
+            if (reader.TokenType == JsonToken.Null)
+            {
+                if (!IsNullableType(objectType))
+                    throw new Exception(String.Format("Cannot convert null value to {0}.",objectType));
+
+                return null;
+            }
+
+            var seconds = double.Parse((string)reader.Value,CultureInfo.InvariantCulture);
+
+            DateTime d =  _epoch.AddSeconds(seconds);
+
+
+#if !PocketPC && !NET20
+            if (t == typeof(DateTimeOffset))
+                return new DateTimeOffset(d);
+#endif
+
+            return d;
+        }
+
+        public static bool IsNullableType(Type t)
+        {
+            if (t== null)
+                throw new ArgumentNullException("t");
+
+            return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
+        }
+    }
+}
\ No newline at end of file
index 4926ff4..9da2d0c 100644 (file)
@@ -3,6 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using NUnit.Framework;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
 using Pithos.Interfaces;
 
 namespace Pithos.Network.Test
@@ -24,5 +26,43 @@ namespace Pithos.Network.Test
             Assert.AreEqual(@"somefolder\file1.txt", path1);
 
         }
+
+
+        [Test]
+        public void TestDeserialization()
+        {
+            var content="[{\"x_object_meta_smoe\": \"true\", \"hash\": \"0f5fbd63a50cb19c13eb111d3c3a7e40\", \"name\": \"wlsetup-web.exe\", \"x_object_meta_moe\": \"true\", \"x_object_meta_joe\": \"true\", \"x_object_sharing\": \"read=lumumba;write=pkanavos,fufutos\", \"x_object_version_timestamp\": \"1319572542.348810\", \"bytes\": 1286504, \"last_modified\": \"2011-10-25T19:55:42.348810+00:00\", \"content_type\": \"application/octet-stream\", \"x_object_version\": 22075, \"x_object_modified_by\": \"890329@vho.grnet.gr\"}]";
+            var infos=JsonConvert.DeserializeObject<IList<ObjectInfo>>(content);
+            var info = infos[0];
+            
+            Assert.IsNotEmpty(info.Tags);
+            Assert.IsNotEmpty(info.Permissions);
+            Assert.AreEqual(3,info.Permissions.Count);
+            Assert.IsTrue(info.Permissions["pkanavos"]=="write");
+            Assert.IsTrue(info.Permissions["lumumba"] == "read");
+            Assert.IsTrue(info.Permissions["fufutos"] == "write");
+            Assert.IsTrue(info.Tags.ContainsKey("x_object_meta_smoe"));
+        }
+
+        [Test]
+        public void ParseInfo()
+        {
+
+            var account = "890329@vho.grnet.gr";
+            var apiKey = "pvCJNuf28+K2UjWnSTlfVQ==";
+            
+            var client = new CloudFilesClient(account, apiKey)
+            {
+                AuthenticationUrl = @"https://pithos.dev.grnet.gr",
+                UsePithos = true
+            };
+            client.Authenticate();
+            var fileName = @"wlsetup-web.exe";
+
+            var infos=client.ListObjects(account, "pithos");
+            var info = infos.First(i=> i.Name == fileName);
+            Assert.IsNotEmpty(info.Tags);
+
+        }
     }
 }
index 6a6dfdd..4b9f967 100644 (file)
@@ -5,8 +5,9 @@ using System.Diagnostics.Contracts;
 using System.IO;
 using System.Text;
 using System.Threading.Tasks;
-using Newtonsoft.Json;
+
 using System.Linq;
+using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 
 namespace Pithos.Network