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
42 using System.Collections.Concurrent;
\r
43 using System.Diagnostics.Contracts;
\r
44 using Pithos.Interfaces;
\r
45 using Pithos.Network;
\r
47 using System.Collections.Generic;
\r
50 namespace Pithos.Core.Agents
\r
54 /// Calculates the differences between two ObjectInfo snapshots.
\r
56 public class SnapshotDifferencer
\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
66 /// The previous snapshot listing
\r
68 public readonly ObjectInfo[] Previous;
\r
71 /// The current snapshot listing
\r
73 public readonly ObjectInfo[] Current;
\r
75 public readonly Dictionary<string, ObjectInfo> CurrentDict;
\r
77 public readonly Dictionary<string, ObjectInfo> PreviousDict;
\r
81 /// Common objects, lazily evalueated.
\r
82 /// The common objects are used to calculate both the Changed and Unchanged objects
\r
84 public readonly Lazy<IEnumerable<ObjectInfo>> Common;
\r
86 private readonly static ObjectInfo[] Empty = new ObjectInfo[0];
\r
88 [ContractInvariantMethod]
\r
89 private void StatInvariant()
\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
100 public State(ObjectInfo[] previous, ObjectInfo[] current)
\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
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
116 private State _state;
\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
122 private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();
\r
125 /// Default constructor. Initializes the Current and Previous listings to empty lists
\r
127 public SnapshotDifferencer()
\r
129 _state = new State(null, null);
\r
133 /// Posts a new listing
\r
135 /// <param name="list"></param>
\r
136 /// <returns></returns>
\r
137 public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)
\r
139 ObjectInfo[] newCurrent=null;
\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
151 //Set the new state
\r
152 _state = new State(_state.Current, newCurrent);
\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
162 public IEnumerable<ObjectInfo> Deleted
\r
164 get { return _state.Previous.Except(_state.Current,_comparer)
\r
165 .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); }
\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
173 public IEnumerable<ObjectInfo> Created
\r
175 get { return _state.Current.Except(_state.Previous,_comparer)
\r
176 .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); }
\r
179 public IEnumerable<ObjectInfo> Common
\r
181 get { return _state.Common.Value; }
\r
184 public IEnumerable<ObjectInfo> Changed
\r
188 return Common.Where(i => i.PreviousHash != i.Hash && (i.Previous == null || i.Uri == i.Previous.Uri));}
\r
190 public IEnumerable<ObjectInfo> Unchanged
\r
192 get{ return Common.Where(i => i.PreviousHash == i.Hash);}
\r
195 public IEnumerable<ObjectInfo> Moved
\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
209 public class AccountsDifferencer
\r
211 readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();
\r
213 public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }
\r
215 public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List<ObjectInfo> cleanRemotes)
\r
217 SnapshotDifferencer differencer;
\r
218 if (!_differencers.TryGetValue(accountInfo.UserName, out differencer))
\r
220 differencer = new SnapshotDifferencer();
\r
221 _differencers[accountInfo.UserName] = differencer;
\r
223 differencer.Post(cleanRemotes);
\r
224 return differencer;
\r