Revision 64171b56

b/trunk/Pithos.Core.Test/SnapshotDifferencerTest.cs
103 103
            var d1 = new SnapshotDifferencer();            
104 104
            var differencer=d1.Post(_previous).Post(_current);
105 105
            var changed = new[] { 
106
                new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name4", Bytes = 1234, Hash = "aa45", Version = 1 },
107
                new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name4", Bytes = 1234, Hash = "aa45", Version = 1 },
106
                new ObjectInfo { Account = "acc1", Container = "Cont1", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1 },
107
                new ObjectInfo { Account = "acc1", Container = "Cont2", Name = "Name4", Bytes = 1234, Hash = "aa45", PreviousHash="aa4", Version = 1 },
108 108
            };
109
            Assert.That(differencer.Changed.ToList(), Is.EquivalentTo(changed)
110
                .Using((IEqualityComparer)new ObjectInfoComparer()));
109

  
110
            Comparison<ObjectInfo> comparer = (x, y) =>
111
                                                  {
112
                                                      if (x.Account == y.Account
113
                                                          && x.Container == y.Container
114
                                                          && x.Name == y.Name
115
                                                          && x.Hash == y.Hash
116
                                                          && x.PreviousHash == y.PreviousHash)
117
                                                          return 0;
118
                                                      return 1;
119
                                                  };
120

  
121
            Assert.That(differencer.Changed.ToList(), Is.EquivalentTo(changed).Using(comparer));
111 122
        }
112 123

  
113 124
        [Test]
b/trunk/Pithos.Core/Agents/SnapshotDifferencer.cs
42 42
using System.Collections.Concurrent;
43 43
using Pithos.Interfaces;
44 44
using Pithos.Network;
45
using System;
46
using System.Collections.Generic;
47
using System.Linq;
45 48

  
46 49
namespace Pithos.Core.Agents
47 50
{
48
    using System;
49
    using System.Collections.Generic;
50
    using System.Linq;
51
    using System.Text;
52 51

  
53 52
    /// <summary>
54
    /// TODO: Update summary.
53
    /// Calculates the differences between two ObjectInfo snapshots.
55 54
    /// </summary>
56 55
    public class SnapshotDifferencer
57 56
    {
58
        private IEnumerable<ObjectInfo> _previous;
59
        private IEnumerable<ObjectInfo> _current;
60
        private static ObjectInfo[] _empty = new ObjectInfo[0];
61
        private ObjectInfoComparer _comparer = new ObjectInfoComparer();
57
        /// <summary>
58
        /// Contains the previous and current listings.
59
        /// Using a separate class for the state simplifies the Post method
60
        /// which has to modify the listings
61
        /// </summary>
62
        private class State
63
        {
64
            /// <summary>
65
            /// The previous snapshot listing
66
            /// </summary>
67
            public readonly IEnumerable<ObjectInfo> Previous;
68

  
69
            /// <summary>
70
            /// The current snapshot listing
71
            /// </summary>
72
            public readonly IEnumerable<ObjectInfo> Current;
73

  
74
            /// <summary>
75
            /// Common objects, lazily evalueated. 
76
            /// The common objects are used to calculate both the Changed and Unchanged objects
77
            /// </summary>
78
            public readonly Lazy<IEnumerable<ObjectInfo>> Common;
79

  
80
            public State(IEnumerable<ObjectInfo> previous, IEnumerable<ObjectInfo> current)
81
            {
82
                Previous = previous ?? new List<ObjectInfo>();
83
                Current = current ?? new List<ObjectInfo>();
84

  
85
                Common=new Lazy<IEnumerable<ObjectInfo>>(() =>
86
                    Current.Join(Previous,
87
                                outKey => new { outKey.Account, outKey.Container, outKey.Name },
88
                                inKey => new { inKey.Account, inKey.Container, inKey.Name },
89
                                (outer, inner) =>
90
                                {
91
                                    outer.PreviousHash = inner.Hash;
92
                                    return outer;
93
                                }));            
94
            }
95
        }
96

  
62 97

  
98
        private State _state;
99

  
100
        /// <summary>
101
        /// The comparer used to identify common objects.
102
        /// Objects are considered common when they have the same Account, Container and Name
103
        /// </summary>
104
        private readonly ObjectInfoComparer _comparer = new ObjectInfoComparer();
105

  
106
        /// <summary>
107
        /// Default constructor. Initializes the Current and Previous listings to empty lists
108
        /// </summary>
63 109
        public SnapshotDifferencer()
110
            :this(new List<ObjectInfo>(),new List<ObjectInfo>())
64 111
        {
65
            _previous = new List<ObjectInfo>();
66
            _current= new List<ObjectInfo>();
67 112
        }
68 113

  
69

  
114
        /// <summary>
115
        /// Creates a new differencer using the specified previous and current list. Null lists
116
        /// are replaced with empty lists
117
        /// </summary>
118
        /// <param name="previous"></param>
119
        /// <param name="current"></param>
70 120
        public SnapshotDifferencer(IEnumerable<ObjectInfo> previous,IEnumerable<ObjectInfo> current  )
71 121
        {
72
            _previous = previous ?? new List<ObjectInfo>();
73
            _current= current ?? new List<ObjectInfo>();
122
            _state=new State(previous,current);
74 123
        }
124

  
125
        /// <summary>
126
        /// Posts a new listing
127
        /// </summary>
128
        /// <param name="list"></param>
129
        /// <returns></returns>
75 130
        public SnapshotDifferencer Post(IEnumerable<ObjectInfo> list)
76 131
        {
77
            _previous = _current;
78
            if (list == null)
132
            List<ObjectInfo> newCurrent=null;
133
            if (list != null)
79 134
            {
80
                _current = new List<ObjectInfo>();
81
                return this;
135
                //The state field holds the old state
136
                var oldState = _state;
137
                //Replace any NoModification entries with previous values that have
138
                //the same account, container and possibly, folder
139
                newCurrent = list.Replace(
140
                    info => info is NoModificationInfo,
141
                    noMod => oldState.Current.Where(noMod.CorrespondsTo))
142
                    .ToList();
82 143
            }
83

  
84

  
85
            //The previous values that correspond to a NoModification object
86
            //have the same account, container and possibly the same folder
87
            Func<ObjectInfo, ObjectInfo, bool> correspondsTo = (info, noMod) => 
88
                info.Account == noMod.Account
89
                && info.Container == noMod.Container
90
                && (noMod.Name == null || info.Name.StartsWith(noMod.Name));
91

  
92
            //Replace any NoModification entries with previous values that have
93
            //the same account, container and possibly, folder
94
            _current=list.Replace(
95
                info => info is NoModificationInfo, 
96
                noMod => _previous.Where(info => correspondsTo(info, noMod)))
97
                .ToList();
144
            //Set the new state
145
            _state = new State(_state.Current, newCurrent);
98 146

  
99 147
            return this;
100 148
        }
101 149
        
102 150
        public IEnumerable<ObjectInfo> Deleted
103 151
        {
104
            get { return _previous.Except(_current,_comparer); }
152
            get { return _state.Previous.Except(_state.Current,_comparer); }
105 153
        }
106 154
        public IEnumerable<ObjectInfo> Created
107 155
        {
108
            get { return _current.Except(_previous,_comparer); }
156
            get { return _state.Current.Except(_state.Previous,_comparer); }
157
        }
158
                
159
        public IEnumerable<ObjectInfo> Common
160
        {
161
            get { return _state.Common.Value; }
109 162
        }
163

  
110 164
        public IEnumerable<ObjectInfo> Changed
111 165
        {
112
            get
113
            {
114
                var changes = from newItem in _current 
115
                              let oldItem=_previous.FirstOrDefault(old=>_comparer.Equals(old,newItem))
116
                              where oldItem !=null &&
117
                                    newItem.Hash != oldItem.Hash
118
                              select newItem;
119
                return changes;
120
            }
166
            get{return Common.Where(i=>i.PreviousHash!=i.Hash);}
121 167
        }
122 168
        public IEnumerable<ObjectInfo> Unchanged
123 169
        {
124
            get
125
            {
126
                var unChanged = from newItem in _current
127
                              let oldItem = _previous.FirstOrDefault(old => _comparer.Equals(old, newItem))
128
                              where oldItem != null &&
129
                                    newItem.Hash == oldItem.Hash
130
                              select newItem;
131
                return unChanged;
132
            }
170
            get{ return Common.Where(i => i.PreviousHash == i.Hash);}
133 171
        }
134 172
    }
135 173

  
136 174
    public class AccountsDifferencer
137 175
    {
138
        ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();
176
        readonly ConcurrentDictionary<string, SnapshotDifferencer> _differencers = new ConcurrentDictionary<string, SnapshotDifferencer>();
139 177

  
140 178
        public ConcurrentDictionary<string, SnapshotDifferencer> Differencers { get { return _differencers; } }
141 179

  
b/trunk/Pithos.Interfaces/ObjectInfo.cs
164 164
        [JsonProperty("X_Object_Public")]
165 165
        public string PublicUrl { get; set; }
166 166

  
167
        public string PreviousHash { get; set; }
168

  
167 169
        public ObjectInfo()
168 170
        {}
169 171

  
......
310 312
            }
311 313
            Permissions = permDict;
312 314
        }
315

  
316
        //The previous values that correspond to a NoModification object
317
        //have the same account, container and possibly the same folder
318
        public bool CorrespondsTo(ObjectInfo other)
319
        {
320
            return other.Account == this.Account
321
                   && other.Container == this.Container
322
                   && (this.Name == null || other.Name.StartsWith(this.Name));
323
        }
313 324
    }
314 325
}

Also available in: Unified diff