Fixed incorrect account key in PollAgent.cs
[pithos-ms-client] / trunk / Pithos.Core / Agents / SnapshotDifferencer.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="SnapshotDifferencer.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\r
7  * Redistribution and use in source and binary forms, with or\r
8  * without modification, are permitted provided that the following\r
9  * conditions are met:\r
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\r
15  *   2. Redistributions in binary form must reproduce the above\r
16  *      copyright notice, this list of conditions and the following\r
17  *      disclaimer in the documentation and/or other materials\r
18  *      provided with the distribution.\r
19  *\r
20  *\r
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
32  * POSSIBILITY OF SUCH DAMAGE.\r
33  *\r
34  * The views and conclusions contained in the software and\r
35  * documentation are those of the authors and should not be\r
36  * interpreted as representing official policies, either expressed\r
37  * or implied, of GRNET S.A.\r
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 \r
43 using System;\r
44 using System.Collections.Concurrent;\r
45 using System.Diagnostics.Contracts;\r
46 using Pithos.Interfaces;\r
47 using Pithos.Network;\r
48 using System.Collections.Generic;\r
49 using System.Linq;\r
50 \r
51 namespace Pithos.Core.Agents\r
52 {\r
53 \r
54     /// <summary>\r
55     /// Calculates the differences between two ObjectInfo snapshots.\r
56     /// </summary>\r
57     public class SnapshotDifferencer\r
58     {\r
59         /// <summary>\r
60         /// Contains the previous and current listings.\r
61         /// Using a separate class for the state simplifies the Post method\r
62         /// which has to modify the listings\r
63         /// </summary>\r
64         private class State\r
65         {\r
66             /// <summary>\r
67             /// The previous snapshot listing\r
68             /// </summary>\r
69             public readonly ObjectInfo[] Previous;\r
70 \r
71             /// <summary>\r
72             /// The current snapshot listing\r
73             /// </summary>\r
74             public readonly ObjectInfo[] Current;\r
75 \r
76             public readonly Dictionary<string, ObjectInfo> CurrentDict;\r
77 \r
78             public readonly Dictionary<string, ObjectInfo> PreviousDict;\r
79 \r
80 \r
81             /// <summary>\r
82             /// Common objects, lazily evalueated. \r
83             /// The common objects are used to calculate both the Changed and Unchanged objects\r
84             /// </summary>\r
85             public readonly IEnumerable<ObjectInfo> Common;\r
86 \r
87             private readonly static ObjectInfo[] Empty = new ObjectInfo[0];\r
88 \r
89             [ContractInvariantMethod]\r
90             private void StatInvariant()\r
91             {\r
92 // ReSharper disable InvocationIsSkipped\r
93                 Contract.Invariant(Current!=null);\r
94                 Contract.Invariant(Previous!=null);\r
95                 Contract.Invariant(CurrentDict!=null);\r
96                 Contract.Invariant(PreviousDict!=null);\r
97                 Contract.Invariant(Common!=null);\r
98 // ReSharper restore InvocationIsSkipped\r
99             }\r
100 \r
101             public State(ObjectInfo[] previous, ObjectInfo[] current)\r
102             {\r
103                 Previous = previous ?? Empty;\r
104                 Current = current ?? Empty;\r
105                 CurrentDict = Current.ToDictionary(info => info.UUID);\r
106                 PreviousDict = Previous.ToDictionary(info => info.UUID);\r
107 \r
108                 //Attach the previous version to the current listings\r
109                 foreach (var info in Current)\r
110                 {\r
111                     ObjectInfo prev;\r
112                     if (PreviousDict.TryGetValue(info.UUID, out prev))\r
113                         info.SetPrevious(prev);\r
114                 }\r
115 \r
116                 Common=Current.Where(c=>c.Previous !=null);\r
117             }\r
118         }\r
119 \r
120 \r
121         private State _state;\r
122 \r
123         /// <summary>\r
124         /// Default constructor. Initializes the Current and Previous listings to empty lists\r
125         /// </summary>\r
126         public SnapshotDifferencer()\r
127         {                        \r
128             _state = new State(null, null);\r
129         }\r
130 \r
131         /// <summary>\r
132         /// Posts a new listing\r
133         /// </summary>\r
134         /// <param name="list"></param>\r
135         /// <returns></returns>\r
136         public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)\r
137         {\r
138             ObjectInfo[] newCurrent=null;\r
139             if (list != null)\r
140             {\r
141                 //The state field holds the old state\r
142                 var oldState = _state;\r
143                 //Replace any NoModification entries with previous values that have\r
144                 //the same account, container and possibly, folder\r
145                 newCurrent = list.Replace(\r
146                     info => info is NoModificationInfo,\r
147                     noMod => oldState.Current.Where(noMod.CorrespondsTo))\r
148                     .ToArray();\r
149             }\r
150             //Set the new state\r
151             _state = new State(_state.Current, newCurrent);\r
152 \r
153             return this;\r
154         }\r
155         \r
156         /// <summary>\r
157         /// Deleted objects are those that existed in the Previous listing\r
158         /// but are not found in the Current listing\r
159         /// </summary>\r
160         public IEnumerable<ObjectInfo> Deleted\r
161         {\r
162             get { return _state.Previous\r
163                 .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }\r
164         }\r
165 \r
166         /// <summary>\r
167         /// Created objects are those that exist in the Current listing\r
168         /// but are not found in the Previous listing \r
169         /// </summary>\r
170         public IEnumerable<ObjectInfo> Created\r
171         {\r
172             get { return _state.Current\r
173                 .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }\r
174         }\r
175                 \r
176         public IEnumerable<ObjectInfo> Common\r
177         {\r
178             get { return _state.Common; }\r
179         }\r
180 \r
181         public IEnumerable<ObjectInfo> Changed\r
182         {\r
183             get\r
184             {\r
185                 return Common.Where(info => \r
186                     //The hash is different\r
187                     info.PreviousHash != info.Hash \r
188                     //And the Uri is unchanged or there is no previous version\r
189                     && (info.Previous == null ||  info.Uri == info.Previous.Uri));\r
190             }\r
191         }\r
192 \r
193         /// <summary>\r
194         /// Unchanged objects have the same current and previous hash\r
195         /// </summary>\r
196         public IEnumerable<ObjectInfo> Unchanged\r
197         {\r
198             get{ return Common.Where(i => i.PreviousHash == i.Hash);}\r
199         }\r
200 \r
201         /// <summary>\r
202         /// Moved objects have a previous version with a different name\r
203         /// </summary>\r
204         public IEnumerable<ObjectInfo>  Moved\r
205         {\r
206             get\r
207             {                                \r
208                 return Common.Where(info=>\r
209                     //A previous version exists\r
210                     info.Previous!= null \r
211                     //and the Uri is different\r
212                     && info.Uri!=info.Previous.Uri);                    \r
213             }\r
214         }\r
215     }\r
216 \r
217     public class AccountsDifferencer\r
218     {\r
219         readonly ConcurrentDictionary<Uri, SnapshotDifferencer> _differencers = new ConcurrentDictionary<Uri, SnapshotDifferencer>();\r
220 \r
221         public ConcurrentDictionary<Uri, SnapshotDifferencer> Differencers { get { return _differencers; } }\r
222 \r
223         public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)\r
224         {\r
225             SnapshotDifferencer differencer;\r
226             if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer))\r
227             {\r
228                 differencer = new SnapshotDifferencer();\r
229                 _differencers[accountInfo.AccountKey] = differencer;\r
230             }\r
231             differencer.Post(cleanRemotes);\r
232             return differencer;\r
233         }\r
234 \r
235     }\r
236 }\r