root / trunk / Pithos.Core / Agents / FileSystemWatcherAdapter.cs @ 8f44fd3a
History | View | Annotate | Download (16 kB)
1 | 255f5f86 | Panagiotis Kanavos | #region |
---|---|---|---|
2 | 255f5f86 | Panagiotis Kanavos | /* ----------------------------------------------------------------------- |
3 | 255f5f86 | Panagiotis Kanavos | * <copyright file="FileSystemWatcherAdapter.cs" company="GRNet"> |
4 | 255f5f86 | Panagiotis Kanavos | * |
5 | 255f5f86 | Panagiotis Kanavos | * Copyright 2011-2012 GRNET S.A. All rights reserved. |
6 | 255f5f86 | Panagiotis Kanavos | * |
7 | 255f5f86 | Panagiotis Kanavos | * Redistribution and use in source and binary forms, with or |
8 | 255f5f86 | Panagiotis Kanavos | * without modification, are permitted provided that the following |
9 | 255f5f86 | Panagiotis Kanavos | * conditions are met: |
10 | 255f5f86 | Panagiotis Kanavos | * |
11 | 255f5f86 | Panagiotis Kanavos | * 1. Redistributions of source code must retain the above |
12 | 255f5f86 | Panagiotis Kanavos | * copyright notice, this list of conditions and the following |
13 | 255f5f86 | Panagiotis Kanavos | * disclaimer. |
14 | 255f5f86 | Panagiotis Kanavos | * |
15 | 255f5f86 | Panagiotis Kanavos | * 2. Redistributions in binary form must reproduce the above |
16 | 255f5f86 | Panagiotis Kanavos | * copyright notice, this list of conditions and the following |
17 | 255f5f86 | Panagiotis Kanavos | * disclaimer in the documentation and/or other materials |
18 | 255f5f86 | Panagiotis Kanavos | * provided with the distribution. |
19 | 255f5f86 | Panagiotis Kanavos | * |
20 | 255f5f86 | Panagiotis Kanavos | * |
21 | 255f5f86 | Panagiotis Kanavos | * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
22 | 255f5f86 | Panagiotis Kanavos | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
23 | 255f5f86 | Panagiotis Kanavos | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | 255f5f86 | Panagiotis Kanavos | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
25 | 255f5f86 | Panagiotis Kanavos | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | 255f5f86 | Panagiotis Kanavos | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | 255f5f86 | Panagiotis Kanavos | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
28 | 255f5f86 | Panagiotis Kanavos | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 | 255f5f86 | Panagiotis Kanavos | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | 255f5f86 | Panagiotis Kanavos | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
31 | 255f5f86 | Panagiotis Kanavos | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
32 | 255f5f86 | Panagiotis Kanavos | * POSSIBILITY OF SUCH DAMAGE. |
33 | 255f5f86 | Panagiotis Kanavos | * |
34 | 255f5f86 | Panagiotis Kanavos | * The views and conclusions contained in the software and |
35 | 255f5f86 | Panagiotis Kanavos | * documentation are those of the authors and should not be |
36 | 255f5f86 | Panagiotis Kanavos | * interpreted as representing official policies, either expressed |
37 | 255f5f86 | Panagiotis Kanavos | * or implied, of GRNET S.A. |
38 | 255f5f86 | Panagiotis Kanavos | * </copyright> |
39 | 255f5f86 | Panagiotis Kanavos | * ----------------------------------------------------------------------- |
40 | 255f5f86 | Panagiotis Kanavos | */ |
41 | 255f5f86 | Panagiotis Kanavos | #endregion |
42 | badcef63 | Panagiotis Kanavos | |
43 | badcef63 | Panagiotis Kanavos | using System.Diagnostics; |
44 | 78ebfd2d | Panagiotis Kanavos | using System.Diagnostics.Contracts; |
45 | 78ebfd2d | Panagiotis Kanavos | using System.IO; |
46 | 174bbb6e | Panagiotis Kanavos | using System.Reflection; |
47 | 78ebfd2d | Panagiotis Kanavos | using System.Threading.Tasks; |
48 | 139ac1e8 | Panagiotis Kanavos | using Pithos.Interfaces; |
49 | 174bbb6e | Panagiotis Kanavos | using log4net; |
50 | 78ebfd2d | Panagiotis Kanavos | |
51 | 78ebfd2d | Panagiotis Kanavos | namespace Pithos.Core.Agents |
52 | 78ebfd2d | Panagiotis Kanavos | { |
53 | 78ebfd2d | Panagiotis Kanavos | using System; |
54 | 78ebfd2d | Panagiotis Kanavos | |
55 | 78ebfd2d | Panagiotis Kanavos | /// <summary> |
56 | 48686774 | Panagiotis Kanavos | /// Wraps a FileSystemWatcher and raises Move and child object events |
57 | 78ebfd2d | Panagiotis Kanavos | /// </summary> |
58 | 78ebfd2d | Panagiotis Kanavos | public class FileSystemWatcherAdapter |
59 | 78ebfd2d | Panagiotis Kanavos | { |
60 | 174bbb6e | Panagiotis Kanavos | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
61 | 174bbb6e | Panagiotis Kanavos | |
62 | 174bbb6e | Panagiotis Kanavos | |
63 | 174bbb6e | Panagiotis Kanavos | public event FileSystemEventHandler Changed; |
64 | 78ebfd2d | Panagiotis Kanavos | public event FileSystemEventHandler Created; |
65 | 78ebfd2d | Panagiotis Kanavos | public event FileSystemEventHandler Deleted; |
66 | 174bbb6e | Panagiotis Kanavos | //public event RenamedEventHandler Renamed; |
67 | 78ebfd2d | Panagiotis Kanavos | public event MovedEventHandler Moved; |
68 | 78ebfd2d | Panagiotis Kanavos | |
69 | 78ebfd2d | Panagiotis Kanavos | public FileSystemWatcherAdapter(FileSystemWatcher watcher) |
70 | 78ebfd2d | Panagiotis Kanavos | { |
71 | 78ebfd2d | Panagiotis Kanavos | if (watcher==null) |
72 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("watcher"); |
73 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
74 | 78ebfd2d | Panagiotis Kanavos | |
75 | 78ebfd2d | Panagiotis Kanavos | watcher.Changed += OnChangeOrCreate; |
76 | 78ebfd2d | Panagiotis Kanavos | watcher.Created += OnChangeOrCreate; |
77 | 78ebfd2d | Panagiotis Kanavos | watcher.Deleted += OnDeleted; |
78 | 174bbb6e | Panagiotis Kanavos | watcher.Renamed += OnRename; |
79 | 174bbb6e | Panagiotis Kanavos | watcher.Error += OnError; |
80 | 174bbb6e | Panagiotis Kanavos | } |
81 | 174bbb6e | Panagiotis Kanavos | |
82 | 174bbb6e | Panagiotis Kanavos | private void OnError(object sender, ErrorEventArgs e) |
83 | 174bbb6e | Panagiotis Kanavos | { |
84 | 174bbb6e | Panagiotis Kanavos | var error = e.GetException(); |
85 | 174bbb6e | Panagiotis Kanavos | Log.Error("FSW error",error); |
86 | 78ebfd2d | Panagiotis Kanavos | } |
87 | 78ebfd2d | Panagiotis Kanavos | |
88 | 78ebfd2d | Panagiotis Kanavos | private string _cachedDeletedFullPath; |
89 | badcef63 | Panagiotis Kanavos | private string CachedDeletedFullPath |
90 | badcef63 | Panagiotis Kanavos | { |
91 | badcef63 | Panagiotis Kanavos | get { return _cachedDeletedFullPath; } |
92 | badcef63 | Panagiotis Kanavos | set |
93 | badcef63 | Panagiotis Kanavos | { |
94 | 6b0de454 | Panagiotis Kanavos | Debug.Assert(Path.IsPathRooted(value)); |
95 | 6b0de454 | Panagiotis Kanavos | if (!Path.IsPathRooted(value)) |
96 | badcef63 | Panagiotis Kanavos | Log.WarnFormat("Storing a relative CachedDeletedFullPath: {0}",value); |
97 | badcef63 | Panagiotis Kanavos | _cachedDeletedFullPath = value; |
98 | badcef63 | Panagiotis Kanavos | } |
99 | badcef63 | Panagiotis Kanavos | } |
100 | badcef63 | Panagiotis Kanavos | |
101 | badcef63 | Panagiotis Kanavos | /// <summary> |
102 | badcef63 | Panagiotis Kanavos | /// Clears any cached deleted file path |
103 | badcef63 | Panagiotis Kanavos | /// </summary> |
104 | badcef63 | Panagiotis Kanavos | /// <remarks> |
105 | badcef63 | Panagiotis Kanavos | /// This method was added to bypass the null checking in the property's setter |
106 | badcef63 | Panagiotis Kanavos | /// </remarks> |
107 | badcef63 | Panagiotis Kanavos | private void ClearCachedDeletedPath() |
108 | badcef63 | Panagiotis Kanavos | { |
109 | badcef63 | Panagiotis Kanavos | _cachedDeletedFullPath = null; |
110 | badcef63 | Panagiotis Kanavos | } |
111 | badcef63 | Panagiotis Kanavos | |
112 | badcef63 | Panagiotis Kanavos | |
113 | 48686774 | Panagiotis Kanavos | private const int PropagateDelay = 10; |
114 | 78ebfd2d | Panagiotis Kanavos | |
115 | 78ebfd2d | Panagiotis Kanavos | private void OnDeleted(object sender, FileSystemEventArgs e) |
116 | 78ebfd2d | Panagiotis Kanavos | { |
117 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
118 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
119 | 78ebfd2d | Panagiotis Kanavos | if (e.ChangeType != WatcherChangeTypes.Deleted) |
120 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
121 | 78ebfd2d | Panagiotis Kanavos | if (string.IsNullOrWhiteSpace(e.FullPath)) |
122 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
123 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
124 | 78ebfd2d | Panagiotis Kanavos | |
125 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerOnDeleted(sender, e)); |
126 | 174bbb6e | Panagiotis Kanavos | } |
127 | 174bbb6e | Panagiotis Kanavos | |
128 | 174bbb6e | Panagiotis Kanavos | private void InnerOnDeleted(object sender, FileSystemEventArgs e) |
129 | 174bbb6e | Panagiotis Kanavos | { |
130 | 174bbb6e | Panagiotis Kanavos | //Handle any previously deleted event |
131 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
132 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]", Enum.GetName(typeof(WatcherChangeTypes), e.ChangeType), e.FullPath); |
133 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
134 | 78ebfd2d | Panagiotis Kanavos | |
135 | 78ebfd2d | Panagiotis Kanavos | //A delete event may be an actual delete event or the first event in a move action. |
136 | 78ebfd2d | Panagiotis Kanavos | //To decide which action occured, we need to wait for the next action, so |
137 | 78ebfd2d | Panagiotis Kanavos | //we store the file path and return . |
138 | 78ebfd2d | Panagiotis Kanavos | //A delete action will not be followed by any other event, so we need to add a watchdog |
139 | 78ebfd2d | Panagiotis Kanavos | //that will declare a Delete action after a short amount of time |
140 | 78ebfd2d | Panagiotis Kanavos | |
141 | 78ebfd2d | Panagiotis Kanavos | //TODO: Moving a folder to the recycle bin results in a single delete event for the entire folder and its contents |
142 | 78ebfd2d | Panagiotis Kanavos | // as this is actually a MOVE operation |
143 | 78ebfd2d | Panagiotis Kanavos | //Deleting by Shift+Delete results in a delete event for each file followed by the delete of the folder itself |
144 | badcef63 | Panagiotis Kanavos | CachedDeletedFullPath = e.FullPath; |
145 | 174bbb6e | Panagiotis Kanavos | |
146 | badcef63 | Panagiotis Kanavos | //TODO: This requires synchronization of the CachedDeletedFullPath field |
147 | 78ebfd2d | Panagiotis Kanavos | //TODO: This creates a new task for each file even though we can cancel any existing tasks if a new event arrives |
148 | 78ebfd2d | Panagiotis Kanavos | //Maybe, use a timer instead of a task |
149 | 174bbb6e | Panagiotis Kanavos | |
150 | 78ebfd2d | Panagiotis Kanavos | TaskEx.Delay(PropagateDelay).ContinueWith(t => |
151 | 174bbb6e | Panagiotis Kanavos | { |
152 | 174bbb6e | Panagiotis Kanavos | var myPath = e.FullPath; |
153 | badcef63 | Panagiotis Kanavos | if (CachedDeletedFullPath == myPath) |
154 | 174bbb6e | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
155 | 174bbb6e | Panagiotis Kanavos | }); |
156 | 78ebfd2d | Panagiotis Kanavos | } |
157 | 78ebfd2d | Panagiotis Kanavos | |
158 | 78ebfd2d | Panagiotis Kanavos | private void OnRename(object sender, RenamedEventArgs e) |
159 | 78ebfd2d | Panagiotis Kanavos | { |
160 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
161 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
162 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
163 | 78ebfd2d | Panagiotis Kanavos | |
164 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerRename(sender, e)); |
165 | 174bbb6e | Panagiotis Kanavos | } |
166 | 174bbb6e | Panagiotis Kanavos | |
167 | 174bbb6e | Panagiotis Kanavos | private void InnerRename(object sender, RenamedEventArgs e) |
168 | 174bbb6e | Panagiotis Kanavos | { |
169 | 78ebfd2d | Panagiotis Kanavos | try |
170 | 78ebfd2d | Panagiotis Kanavos | { |
171 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
172 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]", Enum.GetName(typeof(WatcherChangeTypes), e.ChangeType), e.FullPath); |
173 | 78ebfd2d | Panagiotis Kanavos | //Propagate any previous cached delete event |
174 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
175 | 174bbb6e | Panagiotis Kanavos | |
176 | 174bbb6e | Panagiotis Kanavos | if (Moved!= null) |
177 | 174bbb6e | Panagiotis Kanavos | { |
178 | 174bbb6e | Panagiotis Kanavos | try |
179 | 174bbb6e | Panagiotis Kanavos | { |
180 | 174bbb6e | Panagiotis Kanavos | |
181 | 6b0de454 | Panagiotis Kanavos | Moved(sender, new MovedEventArgs(Path.GetDirectoryName(e.FullPath),Path.GetFileName(e.Name),Path.GetDirectoryName(e.OldFullPath),Path.GetFileName(e.OldName))); |
182 | 174bbb6e | Panagiotis Kanavos | } |
183 | 174bbb6e | Panagiotis Kanavos | catch (Exception exc) |
184 | 174bbb6e | Panagiotis Kanavos | { |
185 | 174bbb6e | Panagiotis Kanavos | Log.Error("Rename event error", exc); |
186 | 174bbb6e | Panagiotis Kanavos | throw; |
187 | 174bbb6e | Panagiotis Kanavos | } |
188 | 174bbb6e | Panagiotis Kanavos | |
189 | 174bbb6e | Panagiotis Kanavos | var directory = new DirectoryInfo(e.FullPath); |
190 | 174bbb6e | Panagiotis Kanavos | if (directory.Exists) |
191 | 174bbb6e | Panagiotis Kanavos | { |
192 | 174bbb6e | Panagiotis Kanavos | var newDirectory = e.FullPath; |
193 | 174bbb6e | Panagiotis Kanavos | var oldDirectory = e.OldFullPath; |
194 | 174bbb6e | Panagiotis Kanavos | |
195 | 174bbb6e | Panagiotis Kanavos | foreach ( |
196 | 174bbb6e | Panagiotis Kanavos | var child in |
197 | 174bbb6e | Panagiotis Kanavos | directory.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) |
198 | 174bbb6e | Panagiotis Kanavos | { |
199 | 174bbb6e | Panagiotis Kanavos | var newChildDirectory = Path.GetDirectoryName(child.FullName); |
200 | 174bbb6e | Panagiotis Kanavos | |
201 | 174bbb6e | Panagiotis Kanavos | var relativePath = child.AsRelativeTo(newDirectory); |
202 | 174bbb6e | Panagiotis Kanavos | var relativeFolder = Path.GetDirectoryName(relativePath); |
203 | 174bbb6e | Panagiotis Kanavos | var oldChildDirectory = Path.Combine(oldDirectory, relativeFolder); |
204 | 174bbb6e | Panagiotis Kanavos | Moved(sender, |
205 | 174bbb6e | Panagiotis Kanavos | new MovedEventArgs(newChildDirectory, child.Name, oldChildDirectory, |
206 | 174bbb6e | Panagiotis Kanavos | child.Name)); |
207 | 174bbb6e | Panagiotis Kanavos | } |
208 | 174bbb6e | Panagiotis Kanavos | } |
209 | 174bbb6e | Panagiotis Kanavos | } |
210 | 78ebfd2d | Panagiotis Kanavos | } |
211 | 78ebfd2d | Panagiotis Kanavos | finally |
212 | 78ebfd2d | Panagiotis Kanavos | { |
213 | badcef63 | Panagiotis Kanavos | ClearCachedDeletedPath(); |
214 | 78ebfd2d | Panagiotis Kanavos | } |
215 | 78ebfd2d | Panagiotis Kanavos | } |
216 | 78ebfd2d | Panagiotis Kanavos | |
217 | 78ebfd2d | Panagiotis Kanavos | private void OnChangeOrCreate(object sender, FileSystemEventArgs e) |
218 | 78ebfd2d | Panagiotis Kanavos | { |
219 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
220 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
221 | 78ebfd2d | Panagiotis Kanavos | if (!(e.ChangeType == WatcherChangeTypes.Created || e.ChangeType == WatcherChangeTypes.Changed)) |
222 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
223 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
224 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerChangeOrCreated(sender, e)); |
225 | 78ebfd2d | Panagiotis Kanavos | |
226 | 174bbb6e | Panagiotis Kanavos | } |
227 | 174bbb6e | Panagiotis Kanavos | |
228 | 174bbb6e | Panagiotis Kanavos | private void InnerChangeOrCreated(object sender, FileSystemEventArgs e) |
229 | 174bbb6e | Panagiotis Kanavos | { |
230 | 78ebfd2d | Panagiotis Kanavos | try |
231 | 78ebfd2d | Panagiotis Kanavos | { |
232 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
233 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]",Enum.GetName(typeof(WatcherChangeTypes),e.ChangeType),e.FullPath); |
234 | 78ebfd2d | Panagiotis Kanavos | //A Move action results in a sequence of a Delete and a Create or Change event |
235 | 78ebfd2d | Panagiotis Kanavos | //If the actual action is a Move, raise a Move event instead of the actual event |
236 | 78ebfd2d | Panagiotis Kanavos | if (HandleMoved(sender, e)) |
237 | 78ebfd2d | Panagiotis Kanavos | return; |
238 | 78ebfd2d | Panagiotis Kanavos | |
239 | 78ebfd2d | Panagiotis Kanavos | //Otherwise, propagate the Delete event if it exists |
240 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
241 | 78ebfd2d | Panagiotis Kanavos | //and propagate the actual event |
242 | 78ebfd2d | Panagiotis Kanavos | var actualEvent = e.ChangeType == WatcherChangeTypes.Created ? Created : Changed; |
243 | 48686774 | Panagiotis Kanavos | |
244 | 78ebfd2d | Panagiotis Kanavos | if (actualEvent != null) |
245 | 48686774 | Panagiotis Kanavos | { |
246 | 78ebfd2d | Panagiotis Kanavos | actualEvent(sender, e); |
247 | 48686774 | Panagiotis Kanavos | //For Folders, raise Created events for all children |
248 | 174bbb6e | Panagiotis Kanavos | RaiseCreatedForChildren(sender, e); |
249 | 48686774 | Panagiotis Kanavos | } |
250 | 78ebfd2d | Panagiotis Kanavos | } |
251 | 78ebfd2d | Panagiotis Kanavos | finally |
252 | 78ebfd2d | Panagiotis Kanavos | { |
253 | 78ebfd2d | Panagiotis Kanavos | //Finally, make sure the cached path is cleared |
254 | badcef63 | Panagiotis Kanavos | ClearCachedDeletedPath(); |
255 | 78ebfd2d | Panagiotis Kanavos | } |
256 | 78ebfd2d | Panagiotis Kanavos | } |
257 | 78ebfd2d | Panagiotis Kanavos | |
258 | badcef63 | Panagiotis Kanavos | |
259 | 48686774 | Panagiotis Kanavos | private void RaiseCreatedForChildren(object sender, FileSystemEventArgs e) |
260 | 48686774 | Panagiotis Kanavos | { |
261 | d78d765c | pkanavos | if(sender==null) |
262 | d78d765c | pkanavos | throw new ArgumentNullException("sender"); |
263 | d78d765c | pkanavos | if (e==null) |
264 | d78d765c | pkanavos | throw new ArgumentNullException("e"); |
265 | d78d765c | pkanavos | Contract.EndContractBlock(); |
266 | 48686774 | Panagiotis Kanavos | |
267 | 48686774 | Panagiotis Kanavos | if (e.ChangeType != WatcherChangeTypes.Created) |
268 | 48686774 | Panagiotis Kanavos | return; |
269 | 48686774 | Panagiotis Kanavos | var dir= new DirectoryInfo(e.FullPath); |
270 | 48686774 | Panagiotis Kanavos | //Skip if this is not a folder |
271 | 48686774 | Panagiotis Kanavos | if (!dir.Exists) |
272 | 48686774 | Panagiotis Kanavos | return; |
273 | 174bbb6e | Panagiotis Kanavos | try |
274 | 48686774 | Panagiotis Kanavos | { |
275 | 174bbb6e | Panagiotis Kanavos | foreach (var info in dir.EnumerateFileSystemInfos("*",SearchOption.AllDirectories)) |
276 | 174bbb6e | Panagiotis Kanavos | { |
277 | 174bbb6e | Panagiotis Kanavos | var path = Path.GetDirectoryName(info.FullName); |
278 | 174bbb6e | Panagiotis Kanavos | Created(sender,new FileSystemEventArgs(WatcherChangeTypes.Created,path,info.Name)); |
279 | 174bbb6e | Panagiotis Kanavos | } |
280 | 174bbb6e | Panagiotis Kanavos | } |
281 | d78d765c | pkanavos | catch (IOException) |
282 | 174bbb6e | Panagiotis Kanavos | { |
283 | 174bbb6e | Panagiotis Kanavos | TaskEx.Delay(1000) |
284 | 174bbb6e | Panagiotis Kanavos | .ContinueWith(_=>RaiseCreatedForChildren(sender,e)); |
285 | 174bbb6e | Panagiotis Kanavos | |
286 | 174bbb6e | Panagiotis Kanavos | } |
287 | 48686774 | Panagiotis Kanavos | } |
288 | 48686774 | Panagiotis Kanavos | |
289 | 78ebfd2d | Panagiotis Kanavos | private bool HandleMoved(object sender, FileSystemEventArgs e) |
290 | 78ebfd2d | Panagiotis Kanavos | { |
291 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
292 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
293 | 78ebfd2d | Panagiotis Kanavos | if (!(e.ChangeType == WatcherChangeTypes.Created || |
294 | 78ebfd2d | Panagiotis Kanavos | e.ChangeType == WatcherChangeTypes.Changed)) |
295 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
296 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
297 | 78ebfd2d | Panagiotis Kanavos | |
298 | 78ebfd2d | Panagiotis Kanavos | //TODO: If a file is deleted and another file with the same name is created, it will be detected as a MOVE |
299 | 78ebfd2d | Panagiotis Kanavos | //instead of a sequence of independent actions |
300 | 78ebfd2d | Panagiotis Kanavos | //One way to detect this would be to request that the full paths are NOT the same |
301 | 78ebfd2d | Panagiotis Kanavos | |
302 | badcef63 | Panagiotis Kanavos | var oldName = Path.GetFileName(CachedDeletedFullPath); |
303 | 78ebfd2d | Panagiotis Kanavos | //NOTE: e.Name is a path relative to the watched path. We MUST call Path.GetFileName to get the actual path |
304 | 78ebfd2d | Panagiotis Kanavos | var newName = Path.GetFileName(e.Name); |
305 | 78ebfd2d | Panagiotis Kanavos | //If the last deleted filename is equal to the current and the action is create, we have a MOVE operation |
306 | badcef63 | Panagiotis Kanavos | var hasMoved = (CachedDeletedFullPath != e.FullPath && oldName == newName); |
307 | 78ebfd2d | Panagiotis Kanavos | |
308 | 78ebfd2d | Panagiotis Kanavos | if (!hasMoved) |
309 | 78ebfd2d | Panagiotis Kanavos | return false; |
310 | 78ebfd2d | Panagiotis Kanavos | |
311 | 78ebfd2d | Panagiotis Kanavos | try |
312 | 78ebfd2d | Panagiotis Kanavos | { |
313 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
314 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("Moved for [{0}]", e.FullPath); |
315 | 174bbb6e | Panagiotis Kanavos | |
316 | 78ebfd2d | Panagiotis Kanavos | //If the actual action is a Move, raise a Move event instead of the actual event |
317 | 78ebfd2d | Panagiotis Kanavos | var newDirectory = Path.GetDirectoryName(e.FullPath); |
318 | badcef63 | Panagiotis Kanavos | var oldDirectory = Path.GetDirectoryName(CachedDeletedFullPath); |
319 | 139ac1e8 | Panagiotis Kanavos | |
320 | 78ebfd2d | Panagiotis Kanavos | if (Moved != null) |
321 | 139ac1e8 | Panagiotis Kanavos | { |
322 | 78ebfd2d | Panagiotis Kanavos | Moved(sender, new MovedEventArgs(newDirectory, newName, oldDirectory, oldName)); |
323 | 139ac1e8 | Panagiotis Kanavos | //If the moved item is a dictionary, we need to raise a change event for each child item |
324 | 139ac1e8 | Panagiotis Kanavos | //When a directory is moved within the same volume, Windows raises events only for the directory object, |
325 | 139ac1e8 | Panagiotis Kanavos | //not its children. This happens because the move actually changes a single directory entry. It doesn't |
326 | 139ac1e8 | Panagiotis Kanavos | //affect the entries of the children. |
327 | 139ac1e8 | Panagiotis Kanavos | var directory = new DirectoryInfo(e.FullPath); |
328 | 139ac1e8 | Panagiotis Kanavos | if (directory.Exists) |
329 | 139ac1e8 | Panagiotis Kanavos | { |
330 | 139ac1e8 | Panagiotis Kanavos | foreach (var child in directory.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) |
331 | 139ac1e8 | Panagiotis Kanavos | { |
332 | 139ac1e8 | Panagiotis Kanavos | var newChildDirectory = Path.GetDirectoryName(child.FullName); |
333 | 139ac1e8 | Panagiotis Kanavos | |
334 | 139ac1e8 | Panagiotis Kanavos | var relativePath=child.AsRelativeTo(newDirectory); |
335 | 139ac1e8 | Panagiotis Kanavos | var relativeFolder = Path.GetDirectoryName(relativePath); |
336 | 139ac1e8 | Panagiotis Kanavos | var oldChildDirectory = Path.Combine(oldDirectory, relativeFolder); |
337 | 139ac1e8 | Panagiotis Kanavos | Moved(sender,new MovedEventArgs(newChildDirectory,child.Name,oldChildDirectory,child.Name)); |
338 | 139ac1e8 | Panagiotis Kanavos | } |
339 | 139ac1e8 | Panagiotis Kanavos | } |
340 | 139ac1e8 | Panagiotis Kanavos | |
341 | 139ac1e8 | Panagiotis Kanavos | } |
342 | 139ac1e8 | Panagiotis Kanavos | |
343 | 78ebfd2d | Panagiotis Kanavos | } |
344 | 78ebfd2d | Panagiotis Kanavos | finally |
345 | 78ebfd2d | Panagiotis Kanavos | { |
346 | badcef63 | Panagiotis Kanavos | ClearCachedDeletedPath(); |
347 | 78ebfd2d | Panagiotis Kanavos | } |
348 | 78ebfd2d | Panagiotis Kanavos | return true; |
349 | 78ebfd2d | Panagiotis Kanavos | } |
350 | 78ebfd2d | Panagiotis Kanavos | |
351 | 78ebfd2d | Panagiotis Kanavos | private void PropagateCachedDeleted(object sender) |
352 | 78ebfd2d | Panagiotis Kanavos | { |
353 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
354 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
355 | badcef63 | Panagiotis Kanavos | Contract.Ensures(CachedDeletedFullPath == null); |
356 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
357 | 78ebfd2d | Panagiotis Kanavos | |
358 | 78ebfd2d | Panagiotis Kanavos | //Nothing to handle if there is no cached deleted file |
359 | badcef63 | Panagiotis Kanavos | if (String.IsNullOrWhiteSpace(CachedDeletedFullPath)) |
360 | 78ebfd2d | Panagiotis Kanavos | return; |
361 | 78ebfd2d | Panagiotis Kanavos | |
362 | badcef63 | Panagiotis Kanavos | var deletedFileName = Path.GetFileName(CachedDeletedFullPath); |
363 | badcef63 | Panagiotis Kanavos | var deletedFileDirectory = Path.GetDirectoryName(CachedDeletedFullPath); |
364 | 78ebfd2d | Panagiotis Kanavos | |
365 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
366 | badcef63 | Panagiotis Kanavos | Log.DebugFormat("Propagating delete for [{0}]", CachedDeletedFullPath); |
367 | 174bbb6e | Panagiotis Kanavos | |
368 | 139ac1e8 | Panagiotis Kanavos | //Only a single file Delete event is raised when moving a file to the Recycle Bin, as this is actually a MOVE operation |
369 | 139ac1e8 | Panagiotis Kanavos | //In this case we need to raise the proper events for all child objects of the deleted directory. |
370 | 139ac1e8 | Panagiotis Kanavos | //UNFORTUNATELY, this can't be detected here, eg. by retrieving the child objects, because they are already deleted |
371 | 139ac1e8 | Panagiotis Kanavos | //This should be done at a higher level, eg by checking the stored state |
372 | 139ac1e8 | Panagiotis Kanavos | if (Deleted != null) |
373 | 139ac1e8 | Panagiotis Kanavos | Deleted(sender,new FileSystemEventArgs(WatcherChangeTypes.Deleted, deletedFileDirectory, deletedFileName)); |
374 | 78ebfd2d | Panagiotis Kanavos | |
375 | badcef63 | Panagiotis Kanavos | ClearCachedDeletedPath(); |
376 | 78ebfd2d | Panagiotis Kanavos | } |
377 | 78ebfd2d | Panagiotis Kanavos | } |
378 | 78ebfd2d | Panagiotis Kanavos | } |