Merge branch 'master' of https://code.grnet.gr/git/pithos-ms-client
[pithos-ms-client] / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs
index 5849624..c340529 100644 (file)
  * -----------------------------------------------------------------------\r
  */\r
 #endregion\r
+\r
+using System;\r
 using System.Collections.Concurrent;\r
+using System.Diagnostics.Contracts;\r
 using Pithos.Interfaces;\r
 using Pithos.Network;\r
-using System;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
 \r
@@ -71,26 +73,47 @@ namespace Pithos.Core.Agents
             /// </summary>\r
             public readonly ObjectInfo[] Current;\r
 \r
+            public readonly Dictionary<string, ObjectInfo> CurrentDict;\r
+\r
+            public readonly Dictionary<string, ObjectInfo> PreviousDict;\r
+\r
+\r
             /// <summary>\r
             /// Common objects, lazily evalueated. \r
             /// The common objects are used to calculate both the Changed and Unchanged objects\r
             /// </summary>\r
-            public readonly Lazy<IEnumerable<ObjectInfo>> Common;\r
+            public readonly IEnumerable<ObjectInfo> Common;\r
+\r
+            private readonly static ObjectInfo[] Empty = new ObjectInfo[0];\r
+\r
+            [ContractInvariantMethod]\r
+            private void StatInvariant()\r
+            {\r
+// ReSharper disable InvocationIsSkipped\r
+                Contract.Invariant(Current!=null);\r
+                Contract.Invariant(Previous!=null);\r
+                Contract.Invariant(CurrentDict!=null);\r
+                Contract.Invariant(PreviousDict!=null);\r
+                Contract.Invariant(Common!=null);\r
+// ReSharper restore InvocationIsSkipped\r
+            }\r
 \r
             public State(ObjectInfo[] previous, ObjectInfo[] current)\r
             {\r
-                Previous = previous ?? new ObjectInfo[0];\r
-                Current = current ?? new ObjectInfo[0];\r
-\r
-                Common=new Lazy<IEnumerable<ObjectInfo>>(() =>\r
-                    Current.Join(Previous,\r
-                                outKey => new { outKey.Account, outKey.Container, outKey.Name },\r
-                                inKey => new { inKey.Account, inKey.Container, inKey.Name },\r
-                                (outer, inner) =>\r
-                                {\r
-                                    outer.PreviousHash = inner.Hash;\r
-                                    return outer;\r
-                                }));            \r
+                Previous = previous ?? Empty;\r
+                Current = current ?? Empty;\r
+                CurrentDict = Current.ToDictionary(info => info.UUID);\r
+                PreviousDict = Previous.ToDictionary(info => info.UUID);\r
+\r
+                //Attach the previous version to the current listings\r
+                foreach (var info in Current)\r
+                {\r
+                    ObjectInfo prev;\r
+                    if (PreviousDict.TryGetValue(info.UUID, out prev))\r
+                        info.SetPrevious(prev);\r
+                }\r
+\r
+                Common=Current.Where(c=>c.Previous !=null);\r
             }\r
         }\r
 \r
@@ -98,18 +121,11 @@ namespace Pithos.Core.Agents
         private State _state;\r
 \r
         /// <summary>\r
-        /// The comparer used to identify common objects.\r
-        /// Objects are considered common when they have the same Account, Container and Name\r
-        /// </summary>\r
-        private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();\r
-\r
-        /// <summary>\r
         /// Default constructor. Initializes the Current and Previous listings to empty lists\r
         /// </summary>\r
         public SnapshotDifferencer()\r
-        {\r
-            var empty = new ObjectInfo[0];            \r
-            _state = new State(empty, empty);\r
+        {                        \r
+            _state = new State(null, null);\r
         }\r
 \r
         /// <summary>\r
@@ -137,43 +153,80 @@ namespace Pithos.Core.Agents
             return this;\r
         }\r
         \r
+        /// <summary>\r
+        /// Deleted objects are those that existed in the Previous listing\r
+        /// but are not found in the Current listing\r
+        /// </summary>\r
         public IEnumerable<ObjectInfo> Deleted\r
         {\r
-            get { return _state.Previous.Except(_state.Current,_comparer); }\r
+            get { return _state.Previous\r
+                .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }\r
         }\r
+\r
+        /// <summary>\r
+        /// Created objects are those that exist in the Current listing\r
+        /// but are not found in the Previous listing \r
+        /// </summary>\r
         public IEnumerable<ObjectInfo> Created\r
         {\r
-            get { return _state.Current.Except(_state.Previous,_comparer); }\r
+            get { return _state.Current\r
+                .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }\r
         }\r
                 \r
         public IEnumerable<ObjectInfo> Common\r
         {\r
-            get { return _state.Common.Value; }\r
+            get { return _state.Common; }\r
         }\r
 \r
         public IEnumerable<ObjectInfo> Changed\r
         {\r
-            get{return Common.Where(i=>i.PreviousHash!=i.Hash);}\r
+            get\r
+            {\r
+                return Common.Where(info => \r
+                    //The hash is different\r
+                    info.PreviousHash != info.Hash \r
+                    //And the Uri is unchanged or there is no previous version\r
+                    && (info.Previous == null ||  info.Uri == info.Previous.Uri));\r
+            }\r
         }\r
+\r
+        /// <summary>\r
+        /// Unchanged objects have the same current and previous hash\r
+        /// </summary>\r
         public IEnumerable<ObjectInfo> Unchanged\r
         {\r
             get{ return Common.Where(i => i.PreviousHash == i.Hash);}\r
         }\r
+\r
+        /// <summary>\r
+        /// Moved objects have a previous version with a different name\r
+        /// </summary>\r
+        public IEnumerable<ObjectInfo>  Moved\r
+        {\r
+            get\r
+            {                                \r
+                return Common.Where(info=>\r
+                    //A previous version exists\r
+                    info.Previous!= null \r
+                    //and the Uri is different\r
+                    && info.Uri!=info.Previous.Uri);                    \r
+            }\r
+        }\r
     }\r
 \r
     public class AccountsDifferencer\r
     {\r
-        readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();\r
+        readonly ConcurrentDictionary<Uri, SnapshotDifferencer> _differencers = new ConcurrentDictionary<Uri, SnapshotDifferencer>();\r
 \r
-        public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }\r
+        public ConcurrentDictionary<Uri, SnapshotDifferencer> Differencers { get { return _differencers; } }\r
 \r
         public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)\r
         {\r
             SnapshotDifferencer differencer;\r
-            if (!_differencers.TryGetValue(accountInfo.UserName, out differencer))\r
+            if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer))\r
             {\r
                 differencer = new SnapshotDifferencer();\r
-                _differencers[accountInfo.UserName] = differencer;\r
+                _differencers[accountInfo.AccountKey] = differencer;\r
             }\r
             differencer.Post(cleanRemotes);\r
             return differencer;\r