#region /* ----------------------------------------------------------------------- * * * Copyright 2011-2012 GRNET S.A. All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and * documentation are those of the authors and should not be * interpreted as representing official policies, either expressed * or implied, of GRNET S.A. * * ----------------------------------------------------------------------- */ #endregion using System; using System.Collections.Concurrent; using System.Diagnostics.Contracts; using Pithos.Interfaces; using Pithos.Network; using System.Collections.Generic; using System.Linq; namespace Pithos.Core.Agents { /// /// Calculates the differences between two ObjectInfo snapshots. /// public class SnapshotDifferencer { /// /// Contains the previous and current listings. /// Using a separate class for the state simplifies the Post method /// which has to modify the listings /// private class State { /// /// The previous snapshot listing /// public readonly ObjectInfo[] Previous; /// /// The current snapshot listing /// public readonly ObjectInfo[] Current; public readonly Dictionary CurrentDict; public readonly Dictionary PreviousDict; /// /// Common objects, lazily evalueated. /// The common objects are used to calculate both the Changed and Unchanged objects /// public readonly IEnumerable Common; private readonly static ObjectInfo[] Empty = new ObjectInfo[0]; [ContractInvariantMethod] private void StatInvariant() { // ReSharper disable InvocationIsSkipped Contract.Invariant(Current!=null); Contract.Invariant(Previous!=null); Contract.Invariant(CurrentDict!=null); Contract.Invariant(PreviousDict!=null); Contract.Invariant(Common!=null); // ReSharper restore InvocationIsSkipped } public State(ObjectInfo[] previous, ObjectInfo[] current) { Previous = previous ?? Empty; Current = current ?? Empty; CurrentDict = Current.ToDictionary(info => info.UUID); PreviousDict = Previous.ToDictionary(info => info.UUID); //Attach the previous version to the current listings foreach (var info in Current) { ObjectInfo prev; if (PreviousDict.TryGetValue(info.UUID, out prev)) info.SetPrevious(prev); } Common=Current.Where(c=>c.Previous !=null); } } private State _state; /// /// Default constructor. Initializes the Current and Previous listings to empty lists /// public SnapshotDifferencer() { _state = new State(null, null); } /// /// Posts a new listing /// /// /// public SnapshotDifferencer Post(IEnumerable list) { ObjectInfo[] newCurrent=null; if (list != null) { //The state field holds the old state var oldState = _state; //Replace any NoModification entries with previous values that have //the same account, container and possibly, folder newCurrent = list.Replace( info => info is NoModificationInfo, noMod => oldState.Current.Where(noMod.CorrespondsTo)) .ToArray(); } //Set the new state _state = new State(_state.Current, newCurrent); return this; } /// /// Deleted objects are those that existed in the Previous listing /// but are not found in the Current listing /// public IEnumerable Deleted { get { return _state.Previous .Where(info=>!_state.CurrentDict.ContainsKey(info.UUID)); } } /// /// Created objects are those that exist in the Current listing /// but are not found in the Previous listing /// public IEnumerable Created { get { return _state.Current .Where(info=>!_state.PreviousDict.ContainsKey(info.UUID)); } } public IEnumerable Common { get { return _state.Common; } } public IEnumerable Changed { get { return Common.Where(info => //The hash is different info.PreviousHash != info.Hash //And the Uri is unchanged or there is no previous version && (info.Previous == null || info.Uri == info.Previous.Uri)); } } /// /// Unchanged objects have the same current and previous hash /// public IEnumerable Unchanged { get{ return Common.Where(i => i.PreviousHash == i.Hash);} } /// /// Moved objects have a previous version with a different name /// public IEnumerable Moved { get { return Common.Where(info=> //A previous version exists info.Previous!= null //and the Uri is different && info.Uri!=info.Previous.Uri); } } } public class AccountsDifferencer { readonly ConcurrentDictionary _differencers = new ConcurrentDictionary(); public ConcurrentDictionary Differencers { get { return _differencers; } } public SnapshotDifferencer PostSnapshot(AccountInfo accountInfo, List cleanRemotes) { SnapshotDifferencer differencer; if (!_differencers.TryGetValue(accountInfo.AccountKey, out differencer)) { differencer = new SnapshotDifferencer(); _differencers[accountInfo.AccountKey] = differencer; } differencer.Post(cleanRemotes); return differencer; } } }