* -----------------------------------------------------------------------\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
/// </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
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
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