Skip to content

Commit 1e4e6f4

Browse files
committed
support RENAMED event
1 parent 35e0881 commit 1e4e6f4

1 file changed

Lines changed: 87 additions & 58 deletions

File tree

Source/FileWatcherEx/EventProcessor.cs

Lines changed: 87 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -39,59 +39,89 @@ class EventProcessor
3939
private bool spamWarningLogged = false;
4040

4141

42-
43-
4442
private IEnumerable<FileChangedEvent> NormalizeEvents(FileChangedEvent[] events)
4543
{
4644
var mapPathToEvents = new Dictionary<string, FileChangedEvent>();
4745
var eventsWithoutDuplicates = new List<FileChangedEvent>();
4846

49-
// Normalize Duplicates
50-
foreach (var e in events)
47+
// Normalize duplicates
48+
foreach (var newEvent in events)
5149
{
50+
mapPathToEvents.TryGetValue(newEvent.FullPath, out var oldEvent); // Try get event from newEvent.FullPath
5251

53-
// Existing event
54-
if (mapPathToEvents.ContainsKey(e.FullPath))
55-
{
56-
var existingEvent = mapPathToEvents[e.FullPath];
57-
var currentChangeType = existingEvent.ChangeType;
58-
var newChangeType = e.ChangeType;
59-
60-
// ignore CREATE followed by DELETE in one go
61-
if (currentChangeType == ChangeType.CREATED && newChangeType == ChangeType.DELETED)
62-
{
63-
mapPathToEvents.Remove(existingEvent.FullPath);
64-
eventsWithoutDuplicates.Remove(existingEvent);
65-
}
52+
if (oldEvent != null && oldEvent.ChangeType == ChangeType.CREATED && newEvent.ChangeType == ChangeType.DELETED)
53+
{ // CREATED + DELETED => remove
54+
mapPathToEvents.Remove(oldEvent.FullPath);
55+
eventsWithoutDuplicates.Remove(oldEvent);
56+
}
57+
else
58+
if (oldEvent != null && oldEvent.ChangeType == ChangeType.DELETED && newEvent.ChangeType == ChangeType.CREATED)
59+
{ // DELETED + CREATED => CHANGED
60+
oldEvent.ChangeType = ChangeType.CHANGED;
61+
}
62+
else
63+
if (oldEvent != null && oldEvent.ChangeType == ChangeType.CREATED && newEvent.ChangeType == ChangeType.CHANGED)
64+
{ // CREATED + CHANGED => CREATED
65+
// Do nothing
66+
}
67+
else
68+
{ // Otherwise
6669

67-
// flatten DELETE followed by CREATE into CHANGE
68-
else if (currentChangeType == ChangeType.DELETED && newChangeType == ChangeType.CREATED)
69-
{
70-
existingEvent.ChangeType = ChangeType.CHANGED;
70+
if (newEvent.ChangeType == ChangeType.RENAMED)
71+
{ // If <ANY> + RENAMED
72+
do
73+
{
74+
mapPathToEvents.TryGetValue(newEvent.OldFullPath, out var renameFromEvent); // Try get event from newEvent.OldFullPath
75+
76+
if (renameFromEvent != null && renameFromEvent.ChangeType == ChangeType.CREATED)
77+
{ // If rename from CREATED file
78+
// Remove data about the CREATED file
79+
mapPathToEvents.Remove(renameFromEvent.FullPath);
80+
eventsWithoutDuplicates.Remove(renameFromEvent);
81+
// Handle new event as CREATED
82+
newEvent.ChangeType = ChangeType.CREATED;
83+
newEvent.OldFullPath = null;
84+
85+
if (oldEvent != null && oldEvent.ChangeType == ChangeType.DELETED)
86+
{ // DELETED + CREATED => CHANGED
87+
newEvent.ChangeType = ChangeType.CHANGED;
88+
}
89+
}
90+
else
91+
if (renameFromEvent != null && renameFromEvent.ChangeType == ChangeType.RENAMED)
92+
{ // If rename from RENAMED file
93+
// Remove data about the RENAMED file
94+
mapPathToEvents.Remove(renameFromEvent.FullPath);
95+
eventsWithoutDuplicates.Remove(renameFromEvent);
96+
// Change OldFullPath
97+
newEvent.OldFullPath = renameFromEvent.OldFullPath;
98+
// Check again
99+
continue;
100+
}
101+
else
102+
{ // Otherwise
103+
// Do nothing
104+
//mapPathToEvents.TryGetValue(newEvent.OldFullPath, out oldEvent); // Try get event from newEvent.OldFullPath
105+
}
106+
} while (false);
71107
}
72108

73-
// Do nothing. Keep the created event
74-
else if (currentChangeType == ChangeType.CREATED && newChangeType == ChangeType.CHANGED)
75-
{
109+
if (oldEvent != null)
110+
{ // If old event exists
111+
// Replace old event data with data from the new event
112+
oldEvent.ChangeType = newEvent.ChangeType;
113+
oldEvent.OldFullPath = newEvent.OldFullPath;
76114
}
77-
78-
// Otherwise apply change type
79115
else
80-
{
81-
existingEvent.ChangeType = newChangeType;
116+
{ // If old event is not exist
117+
// Add new event
118+
mapPathToEvents.Add(newEvent.FullPath, newEvent);
119+
eventsWithoutDuplicates.Add(newEvent);
82120
}
83121
}
84-
85-
// New event
86-
else
87-
{
88-
mapPathToEvents.Add(e.FullPath, e);
89-
eventsWithoutDuplicates.Add(e);
90-
}
91122
}
92123

93124
// Handle deletes
94-
var addedChangeEvents = new List<FileChangedEvent>();
95125
var deletedPaths = new List<string>();
96126

97127
// This algorithm will remove all DELETE events up to the root folder
@@ -103,30 +133,25 @@ private IEnumerable<FileChangedEvent> NormalizeEvents(FileChangedEvent[] events)
103133
// 3.) for each DELETE, check if there is a deleted parent and ignore the event in that case
104134

105135
return eventsWithoutDuplicates
106-
.Where((e) =>
136+
.Select((e, n) => new KeyValuePair<int, FileChangedEvent>(n, e)) // store original position value
137+
.OrderBy(e => e.Value.FullPath.Length) // shortest path first
138+
.Where(e =>
107139
{
108-
if (e.ChangeType != ChangeType.DELETED)
140+
if (e.Value.ChangeType == ChangeType.DELETED)
109141
{
110-
addedChangeEvents.Add(e);
111-
return false; // remove ADD / CHANGE
112-
}
142+
if (deletedPaths.Any(d => IsParent(e.Value.FullPath, d)))
143+
{
144+
return false; // DELETE is ignored if parent is deleted already
145+
}
113146

114-
return true;
115-
})
116-
.OrderBy((e) => e.FullPath.Length) // shortest path first
117-
.Where((e) =>
118-
{
119-
if (deletedPaths.Any(d => IsParent(e.FullPath, d)))
120-
{
121-
return false; // DELETE is ignored if parent is deleted already
147+
// otherwise mark as deleted
148+
deletedPaths.Add(e.Value.FullPath);
122149
}
123150

124-
// otherwise mark as deleted
125-
deletedPaths.Add(e.FullPath);
126-
127151
return true;
128152
})
129-
.Concat(addedChangeEvents);
153+
.OrderBy(e => e.Key) // restore orinal position
154+
.Select(e => e.Value); // remove unnecessary position value
130155
}
131156

132157

@@ -152,10 +177,13 @@ public void ProcessEvent(FileChangedEvent fileEvent)
152177
var now = DateTime.Now.Ticks;
153178

154179
// Check for spam
155-
if (events.Count == 0) {
180+
if (events.Count == 0)
181+
{
156182
spamWarningLogged = false;
157183
spamCheckStartTime = now;
158-
} else if (!spamWarningLogged && spamCheckStartTime + EVENT_SPAM_WARNING_THRESHOLD < now) {
184+
}
185+
else if (!spamWarningLogged && spamCheckStartTime + EVENT_SPAM_WARNING_THRESHOLD < now)
186+
{
159187
spamWarningLogged = true;
160188
logger(string.Format("Warning: Watcher is busy catching up wit {0} file changes in 60 seconds. Latest path is '{1}'", events.Count, fileEvent.FullPath));
161189
}
@@ -169,7 +197,8 @@ public void ProcessEvent(FileChangedEvent fileEvent)
169197
{
170198
// Create function to buffer events
171199
Action<Task> func = null;
172-
func = (Task value) => {
200+
func = (Task value) =>
201+
{
173202
lock (LOCK)
174203
{
175204
// Check if another event has been received in the meantime
@@ -199,11 +228,11 @@ public void ProcessEvent(FileChangedEvent fileEvent)
199228

200229
// Start function after delay
201230
delayStarted = lastEventTime;
202-
delayTask = Task.Delay(EVENT_DELAY).ContinueWith(func);
231+
delayTask = Task.Delay(EVENT_DELAY).ContinueWith(func);
203232
}
204233
}
205234
}
206235

207-
236+
208237
}
209238
}

0 commit comments

Comments
 (0)