@@ -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