2 /* -----------------------------------------------------------------------
\r
3 * <copyright file="SnapshotDifferencer.cs" company="GRNet">
\r
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
\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
11 * 1. Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\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
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
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
39 * -----------------------------------------------------------------------
\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
51 namespace Pithos.Core.Agents
\r
55 /// Calculates the differences between two ObjectInfo snapshots.
\r
57 public class SnapshotDifferencer
\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
67 /// The previous snapshot listing
\r
69 public readonly ObjectInfo[] Previous;
\r
72 /// The current snapshot listing
\r
74 public readonly ObjectInfo[] Current;
\r
76 public readonly Dictionary<string, ObjectInfo> CurrentDict;
\r
78 public readonly Dictionary<string, ObjectInfo> PreviousDict;
\r
82 /// Common objects, lazily evalueated.
\r
83 /// The common objects are used to calculate both the Changed and Unchanged objects
\r
85 public readonly IEnumerable<ObjectInfo> Common;
\r
87 private readonly static ObjectInfo[] Empty = new ObjectInfo[0];
\r
89 [ContractInvariantMethod]
\r
90 private void StatInvariant()
\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
101 public State(ObjectInfo[] previous, ObjectInfo[] current)
\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
108 //Attach the previous version to the current listings
\r
109 foreach (var info in Current)
\r
112 if (PreviousDict.TryGetValue(info.UUID, out prev))
\r
113 info.SetPrevious(prev);
\r
116 Common=Current.Where(c=>c.Previous !=null);
\r
121 private State _state;
\r
124 /// Default constructor. Initializes the Current and Previous listings to empty lists
\r
126 public SnapshotDifferencer()
\r
128 _state = new State(null, null);
\r
132 /// Posts a new listing
\r
134 /// <param name="list"></param>
\r
135 /// <returns></returns>
\r
136 public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)
\r
138 ObjectInfo[] newCurrent=null;
\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
150 //Set the new state
\r
151 _state = new State(_state.Current, newCurrent);
\r
157 /// Deleted objects are those that existed in the Previous listing
\r
158 /// but are not found in the Current listing
\r
160 public IEnumerable<ObjectInfo> Deleted
\r
162 get { return _state.Previous
\r
163 .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
\r
167 /// Created objects are those that exist in the Current listing
\r
168 /// but are not found in the Previous listing
\r
170 public IEnumerable<ObjectInfo> Created
\r
172 get { return _state.Current
\r
173 .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
\r
176 public IEnumerable<ObjectInfo> Common
\r
178 get { return _state.Common; }
\r
181 public IEnumerable<ObjectInfo> Changed
\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
194 /// Unchanged objects have the same current and previous hash
\r
196 public IEnumerable<ObjectInfo> Unchanged
\r
198 get{ return Common.Where(i => i.PreviousHash == i.Hash);}
\r
202 /// Moved objects have a previous version with a different name
\r
204 public IEnumerable<ObjectInfo> Moved
\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
217 public class AccountsDifferencer
\r
219 readonly ConcurrentDictionary<Uri, SnapshotDifferencer> _differencers = new ConcurrentDictionary<Uri, SnapshotDifferencer>();
\r
221 public ConcurrentDictionary<Uri, SnapshotDifferencer> Differencers { get { return _differencers; } }
\r
223 public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
\r
225 SnapshotDifferencer differencer;
\r
226 if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer))
\r
228 differencer = new SnapshotDifferencer();
\r
229 _differencers[accountInfo.AccountKey] = differencer;
\r
231 differencer.Post(cleanRemotes);
\r
232 return differencer;
\r