7bd83b9bb93adffc255b25a47f404b3938eb3628
[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 using System.Collections.Concurrent;\r
43 using System.Diagnostics.Contracts;\r
44 using Pithos.Interfaces;\r
45 using Pithos.Network;\r
46 using System;\r
47 using System.Collections.Generic;\r
48 using System.Linq;\r
49 \r
50 namespace Pithos.Core.Agents\r
51 {\r
52 \r
53     /// <summary>\r
54     /// Calculates the differences between two ObjectInfo snapshots.\r
55     /// </summary>\r
56     public class SnapshotDifferencer\r
57     {\r
58         /// <summary>\r
59         /// Contains the previous and current listings.\r
60         /// Using a separate class for the state simplifies the Post method\r
61         /// which has to modify the listings\r
62         /// </summary>\r
63         private class State\r
64         {\r
65             /// <summary>\r
66             /// The previous snapshot listing\r
67             /// </summary>\r
68             public readonly ObjectInfo[] Previous;\r
69 \r
70             /// <summary>\r
71             /// The current snapshot listing\r
72             /// </summary>\r
73             public readonly ObjectInfo[] Current;\r
74 \r
75             public readonly Dictionary<string, ObjectInfo> CurrentDict;\r
76 \r
77             public readonly Dictionary<string, ObjectInfo> PreviousDict;\r
78 \r
79 \r
80             /// <summary>\r
81             /// Common objects, lazily evalueated. \r
82             /// The common objects are used to calculate both the Changed and Unchanged objects\r
83             /// </summary>\r
84             public readonly Lazy<IEnumerable<ObjectInfo>> Common;\r
85 \r
86             private readonly static ObjectInfo[] Empty = new ObjectInfo[0];\r
87 \r
88             [ContractInvariantMethod]\r
89             private void StatInvariant()\r
90             {\r
91 // ReSharper disable InvocationIsSkipped\r
92                 Contract.Invariant(Current!=null);\r
93                 Contract.Invariant(Previous!=null);\r
94                 Contract.Invariant(CurrentDict!=null);\r
95                 Contract.Invariant(PreviousDict!=null);\r
96                 Contract.Invariant(Common!=null);\r
97 // ReSharper restore InvocationIsSkipped\r
98             }\r
99 \r
100             public State(ObjectInfo[] previous, ObjectInfo[] current)\r
101             {\r
102                 Previous = previous ?? Empty;\r
103                 Current = current ?? Empty;\r
104                 CurrentDict = Current.ToDictionary(info => info.UUID);\r
105                 PreviousDict = Previous.ToDictionary(info => info.UUID);\r
106 \r
107                 Common=new Lazy<IEnumerable<ObjectInfo>>(() =>\r
108                     Current.Join(Previous,\r
109                                 outKey => outKey.UUID,\r
110                                 inKey => inKey.UUID,\r
111                                 (outer, inner) =>outer.SetPrevious(inner)));            \r
112             }\r
113         }\r
114 \r
115 \r
116         private State _state;\r
117 \r
118         /// <summary>\r
119         /// The comparer used to identify common objects.\r
120         /// Objects are considered common when they have the same Account, Container and Name\r
121         /// </summary>\r
122         private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();\r
123 \r
124         /// <summary>\r
125         /// Default constructor. Initializes the Current and Previous listings to empty lists\r
126         /// </summary>\r
127         public SnapshotDifferencer()\r
128         {                        \r
129             _state = new State(null, null);\r
130         }\r
131 \r
132         /// <summary>\r
133         /// Posts a new listing\r
134         /// </summary>\r
135         /// <param name="list"></param>\r
136         /// <returns></returns>\r
137         public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)\r
138         {\r
139             ObjectInfo[] newCurrent=null;\r
140             if (list != null)\r
141             {\r
142                 //The state field holds the old state\r
143                 var oldState = _state;\r
144                 //Replace any NoModification entries with previous values that have\r
145                 //the same account, container and possibly, folder\r
146                 newCurrent = list.Replace(\r
147                     info => info is NoModificationInfo,\r
148                     noMod => oldState.Current.Where(noMod.CorrespondsTo))\r
149                     .ToArray();\r
150             }\r
151             //Set the new state\r
152             _state = new State(_state.Current, newCurrent);\r
153 \r
154             return this;\r
155         }\r
156         \r
157         /// <summary>\r
158         /// Deleted objects are those that existed in the Previous listing\r
159         /// but are not found in the Current listing, and their UUIDs do not\r
160         /// appear in the Current listing (ie they were not renamed/moved)\r
161         /// </summary>\r
162         public IEnumerable<ObjectInfo> Deleted\r
163         {\r
164             get { return _state.Previous.Except(_state.Current,_comparer)\r
165                 .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }\r
166         }\r
167 \r
168         /// <summary>\r
169         /// Created objects are those that exist in the Current listing\r
170         /// but are not found in the Previous listing, and their UUIDs do not\r
171         /// appear in the Previous listing (ie they were not renamed/moved)\r
172         /// </summary>\r
173         public IEnumerable<ObjectInfo> Created\r
174         {\r
175             get { return _state.Current.Except(_state.Previous,_comparer)\r
176                 .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }\r
177         }\r
178                 \r
179         public IEnumerable<ObjectInfo> Common\r
180         {\r
181             get { return _state.Common.Value; }\r
182         }\r
183 \r
184         public IEnumerable<ObjectInfo> Changed\r
185         {\r
186             get\r
187             {\r
188                 return Common.Where(i => i.PreviousHash != i.Hash && (i.Previous == null ||  i.Uri == i.Previous.Uri));}\r
189         }\r
190         public IEnumerable<ObjectInfo> Unchanged\r
191         {\r
192             get{ return Common.Where(i => i.PreviousHash == i.Hash);}\r
193         }\r
194 \r
195         public IEnumerable<ObjectInfo>  Moved\r
196         {\r
197             get\r
198             {\r
199                                 \r
200                 return _state.Current.Join(_state.Previous,\r
201                                     outer => outer.UUID,\r
202                                     inner => inner.UUID,\r
203                                     (outer, inner) => (outer.Name == inner.Name ? null : outer.SetPrevious(inner)))\r
204                     .Where(t => t != null);                \r
205             }\r
206         }\r
207     }\r
208 \r
209     public class AccountsDifferencer\r
210     {\r
211         readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();\r
212 \r
213         public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }\r
214 \r
215         public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)\r
216         {\r
217             SnapshotDifferencer differencer;\r
218             if (!_differencers.TryGetValue(accountInfo.UserName, out differencer))\r
219             {\r
220                 differencer = new SnapshotDifferencer();\r
221                 _differencers[accountInfo.UserName] = differencer;\r
222             }\r
223             differencer.Post(cleanRemotes);\r
224             return differencer;\r
225         }\r
226 \r
227     }\r
228 }\r