root / trunk / Pithos.Core / Agents / FileSystemWatcherAdapter.cs @ dccd340f
History | View | Annotate | Download (15 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 | 78ebfd2d | Panagiotis Kanavos | using System.Diagnostics.Contracts; |
43 | 78ebfd2d | Panagiotis Kanavos | using System.IO; |
44 | 174bbb6e | Panagiotis Kanavos | using System.Reflection; |
45 | 78ebfd2d | Panagiotis Kanavos | using System.Threading.Tasks; |
46 | 139ac1e8 | Panagiotis Kanavos | using Pithos.Interfaces; |
47 | 174bbb6e | Panagiotis Kanavos | using log4net; |
48 | 78ebfd2d | Panagiotis Kanavos | |
49 | 78ebfd2d | Panagiotis Kanavos | namespace Pithos.Core.Agents |
50 | 78ebfd2d | Panagiotis Kanavos | { |
51 | 78ebfd2d | Panagiotis Kanavos | using System; |
52 | 78ebfd2d | Panagiotis Kanavos | |
53 | 78ebfd2d | Panagiotis Kanavos | /// <summary> |
54 | 48686774 | Panagiotis Kanavos | /// Wraps a FileSystemWatcher and raises Move and child object events |
55 | 78ebfd2d | Panagiotis Kanavos | /// </summary> |
56 | 78ebfd2d | Panagiotis Kanavos | public class FileSystemWatcherAdapter |
57 | 78ebfd2d | Panagiotis Kanavos | { |
58 | 174bbb6e | Panagiotis Kanavos | private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
59 | 174bbb6e | Panagiotis Kanavos | |
60 | 174bbb6e | Panagiotis Kanavos | |
61 | 174bbb6e | Panagiotis Kanavos | public event FileSystemEventHandler Changed; |
62 | 78ebfd2d | Panagiotis Kanavos | public event FileSystemEventHandler Created; |
63 | 78ebfd2d | Panagiotis Kanavos | public event FileSystemEventHandler Deleted; |
64 | 174bbb6e | Panagiotis Kanavos | //public event RenamedEventHandler Renamed; |
65 | 78ebfd2d | Panagiotis Kanavos | public event MovedEventHandler Moved; |
66 | 78ebfd2d | Panagiotis Kanavos | |
67 | 78ebfd2d | Panagiotis Kanavos | public FileSystemWatcherAdapter(FileSystemWatcher watcher) |
68 | 78ebfd2d | Panagiotis Kanavos | { |
69 | 78ebfd2d | Panagiotis Kanavos | if (watcher==null) |
70 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("watcher"); |
71 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
72 | 78ebfd2d | Panagiotis Kanavos | |
73 | 78ebfd2d | Panagiotis Kanavos | watcher.Changed += OnChangeOrCreate; |
74 | 78ebfd2d | Panagiotis Kanavos | watcher.Created += OnChangeOrCreate; |
75 | 78ebfd2d | Panagiotis Kanavos | watcher.Deleted += OnDeleted; |
76 | 174bbb6e | Panagiotis Kanavos | watcher.Renamed += OnRename; |
77 | 174bbb6e | Panagiotis Kanavos | watcher.Error += OnError; |
78 | 174bbb6e | Panagiotis Kanavos | } |
79 | 174bbb6e | Panagiotis Kanavos | |
80 | 174bbb6e | Panagiotis Kanavos | private void OnError(object sender, ErrorEventArgs e) |
81 | 174bbb6e | Panagiotis Kanavos | { |
82 | 174bbb6e | Panagiotis Kanavos | var error = e.GetException(); |
83 | 174bbb6e | Panagiotis Kanavos | Log.Error("FSW error",error); |
84 | 78ebfd2d | Panagiotis Kanavos | } |
85 | 78ebfd2d | Panagiotis Kanavos | |
86 | 78ebfd2d | Panagiotis Kanavos | private string _cachedDeletedFullPath; |
87 | 48686774 | Panagiotis Kanavos | private const int PropagateDelay = 10; |
88 | 78ebfd2d | Panagiotis Kanavos | |
89 | 78ebfd2d | Panagiotis Kanavos | private void OnDeleted(object sender, FileSystemEventArgs e) |
90 | 78ebfd2d | Panagiotis Kanavos | { |
91 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
92 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
93 | 78ebfd2d | Panagiotis Kanavos | if (e.ChangeType != WatcherChangeTypes.Deleted) |
94 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
95 | 78ebfd2d | Panagiotis Kanavos | if (string.IsNullOrWhiteSpace(e.FullPath)) |
96 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
97 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
98 | 78ebfd2d | Panagiotis Kanavos | |
99 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerOnDeleted(sender, e)); |
100 | 174bbb6e | Panagiotis Kanavos | } |
101 | 174bbb6e | Panagiotis Kanavos | |
102 | 174bbb6e | Panagiotis Kanavos | private void InnerOnDeleted(object sender, FileSystemEventArgs e) |
103 | 174bbb6e | Panagiotis Kanavos | { |
104 | 174bbb6e | Panagiotis Kanavos | //Handle any previously deleted event |
105 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
106 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]", Enum.GetName(typeof(WatcherChangeTypes), e.ChangeType), e.FullPath); |
107 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
108 | 78ebfd2d | Panagiotis Kanavos | |
109 | 78ebfd2d | Panagiotis Kanavos | //A delete event may be an actual delete event or the first event in a move action. |
110 | 78ebfd2d | Panagiotis Kanavos | //To decide which action occured, we need to wait for the next action, so |
111 | 78ebfd2d | Panagiotis Kanavos | //we store the file path and return . |
112 | 78ebfd2d | Panagiotis Kanavos | //A delete action will not be followed by any other event, so we need to add a watchdog |
113 | 78ebfd2d | Panagiotis Kanavos | //that will declare a Delete action after a short amount of time |
114 | 78ebfd2d | Panagiotis Kanavos | |
115 | 78ebfd2d | Panagiotis Kanavos | //TODO: Moving a folder to the recycle bin results in a single delete event for the entire folder and its contents |
116 | 78ebfd2d | Panagiotis Kanavos | // as this is actually a MOVE operation |
117 | 78ebfd2d | Panagiotis Kanavos | //Deleting by Shift+Delete results in a delete event for each file followed by the delete of the folder itself |
118 | 78ebfd2d | Panagiotis Kanavos | _cachedDeletedFullPath = e.FullPath; |
119 | 174bbb6e | Panagiotis Kanavos | |
120 | 78ebfd2d | Panagiotis Kanavos | //TODO: This requires synchronization of the _cachedDeletedFullPath field |
121 | 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 |
122 | 78ebfd2d | Panagiotis Kanavos | //Maybe, use a timer instead of a task |
123 | 174bbb6e | Panagiotis Kanavos | |
124 | 78ebfd2d | Panagiotis Kanavos | TaskEx.Delay(PropagateDelay).ContinueWith(t => |
125 | 174bbb6e | Panagiotis Kanavos | { |
126 | 174bbb6e | Panagiotis Kanavos | var myPath = e.FullPath; |
127 | 174bbb6e | Panagiotis Kanavos | if (_cachedDeletedFullPath == myPath) |
128 | 174bbb6e | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
129 | 174bbb6e | Panagiotis Kanavos | }); |
130 | 78ebfd2d | Panagiotis Kanavos | } |
131 | 78ebfd2d | Panagiotis Kanavos | |
132 | 78ebfd2d | Panagiotis Kanavos | private void OnRename(object sender, RenamedEventArgs e) |
133 | 78ebfd2d | Panagiotis Kanavos | { |
134 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
135 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
136 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
137 | 78ebfd2d | Panagiotis Kanavos | |
138 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerRename(sender, e)); |
139 | 174bbb6e | Panagiotis Kanavos | } |
140 | 174bbb6e | Panagiotis Kanavos | |
141 | 174bbb6e | Panagiotis Kanavos | private void InnerRename(object sender, RenamedEventArgs e) |
142 | 174bbb6e | Panagiotis Kanavos | { |
143 | 78ebfd2d | Panagiotis Kanavos | try |
144 | 78ebfd2d | Panagiotis Kanavos | { |
145 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
146 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]", Enum.GetName(typeof(WatcherChangeTypes), e.ChangeType), e.FullPath); |
147 | 78ebfd2d | Panagiotis Kanavos | //Propagate any previous cached delete event |
148 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
149 | 174bbb6e | Panagiotis Kanavos | |
150 | 174bbb6e | Panagiotis Kanavos | if (Moved!= null) |
151 | 174bbb6e | Panagiotis Kanavos | { |
152 | 174bbb6e | Panagiotis Kanavos | try |
153 | 174bbb6e | Panagiotis Kanavos | { |
154 | 174bbb6e | Panagiotis Kanavos | |
155 | 174bbb6e | Panagiotis Kanavos | Moved(sender, new MovedEventArgs(e.FullPath,e.Name,e.OldFullPath,e.OldName)); |
156 | 174bbb6e | Panagiotis Kanavos | } |
157 | 174bbb6e | Panagiotis Kanavos | catch (Exception exc) |
158 | 174bbb6e | Panagiotis Kanavos | { |
159 | 174bbb6e | Panagiotis Kanavos | Log.Error("Rename event error", exc); |
160 | 174bbb6e | Panagiotis Kanavos | throw; |
161 | 174bbb6e | Panagiotis Kanavos | } |
162 | 174bbb6e | Panagiotis Kanavos | |
163 | 174bbb6e | Panagiotis Kanavos | var directory = new DirectoryInfo(e.FullPath); |
164 | 174bbb6e | Panagiotis Kanavos | if (directory.Exists) |
165 | 174bbb6e | Panagiotis Kanavos | { |
166 | 174bbb6e | Panagiotis Kanavos | var newDirectory = e.FullPath; |
167 | 174bbb6e | Panagiotis Kanavos | var oldDirectory = e.OldFullPath; |
168 | 174bbb6e | Panagiotis Kanavos | |
169 | 174bbb6e | Panagiotis Kanavos | foreach ( |
170 | 174bbb6e | Panagiotis Kanavos | var child in |
171 | 174bbb6e | Panagiotis Kanavos | directory.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) |
172 | 174bbb6e | Panagiotis Kanavos | { |
173 | 174bbb6e | Panagiotis Kanavos | var newChildDirectory = Path.GetDirectoryName(child.FullName); |
174 | 174bbb6e | Panagiotis Kanavos | |
175 | 174bbb6e | Panagiotis Kanavos | var relativePath = child.AsRelativeTo(newDirectory); |
176 | 174bbb6e | Panagiotis Kanavos | var relativeFolder = Path.GetDirectoryName(relativePath); |
177 | 174bbb6e | Panagiotis Kanavos | var oldChildDirectory = Path.Combine(oldDirectory, relativeFolder); |
178 | 174bbb6e | Panagiotis Kanavos | Moved(sender, |
179 | 174bbb6e | Panagiotis Kanavos | new MovedEventArgs(newChildDirectory, child.Name, oldChildDirectory, |
180 | 174bbb6e | Panagiotis Kanavos | child.Name)); |
181 | 174bbb6e | Panagiotis Kanavos | } |
182 | 174bbb6e | Panagiotis Kanavos | } |
183 | 174bbb6e | Panagiotis Kanavos | } |
184 | 78ebfd2d | Panagiotis Kanavos | } |
185 | 78ebfd2d | Panagiotis Kanavos | finally |
186 | 78ebfd2d | Panagiotis Kanavos | { |
187 | 174bbb6e | Panagiotis Kanavos | _cachedDeletedFullPath = null; |
188 | 78ebfd2d | Panagiotis Kanavos | } |
189 | 78ebfd2d | Panagiotis Kanavos | } |
190 | 78ebfd2d | Panagiotis Kanavos | |
191 | 78ebfd2d | Panagiotis Kanavos | private void OnChangeOrCreate(object sender, FileSystemEventArgs e) |
192 | 78ebfd2d | Panagiotis Kanavos | { |
193 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
194 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
195 | 78ebfd2d | Panagiotis Kanavos | if (!(e.ChangeType == WatcherChangeTypes.Created || e.ChangeType == WatcherChangeTypes.Changed)) |
196 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
197 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
198 | 174bbb6e | Panagiotis Kanavos | TaskEx.Run(() => InnerChangeOrCreated(sender, e)); |
199 | 78ebfd2d | Panagiotis Kanavos | |
200 | 174bbb6e | Panagiotis Kanavos | } |
201 | 174bbb6e | Panagiotis Kanavos | |
202 | 174bbb6e | Panagiotis Kanavos | private void InnerChangeOrCreated(object sender, FileSystemEventArgs e) |
203 | 174bbb6e | Panagiotis Kanavos | { |
204 | 78ebfd2d | Panagiotis Kanavos | try |
205 | 78ebfd2d | Panagiotis Kanavos | { |
206 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
207 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("[{0}] for [{1}]",Enum.GetName(typeof(WatcherChangeTypes),e.ChangeType),e.FullPath); |
208 | 78ebfd2d | Panagiotis Kanavos | //A Move action results in a sequence of a Delete and a Create or Change event |
209 | 78ebfd2d | Panagiotis Kanavos | //If the actual action is a Move, raise a Move event instead of the actual event |
210 | 78ebfd2d | Panagiotis Kanavos | if (HandleMoved(sender, e)) |
211 | 78ebfd2d | Panagiotis Kanavos | return; |
212 | 78ebfd2d | Panagiotis Kanavos | |
213 | 78ebfd2d | Panagiotis Kanavos | //Otherwise, propagate the Delete event if it exists |
214 | 78ebfd2d | Panagiotis Kanavos | PropagateCachedDeleted(sender); |
215 | 78ebfd2d | Panagiotis Kanavos | //and propagate the actual event |
216 | 78ebfd2d | Panagiotis Kanavos | var actualEvent = e.ChangeType == WatcherChangeTypes.Created ? Created : Changed; |
217 | 48686774 | Panagiotis Kanavos | |
218 | 78ebfd2d | Panagiotis Kanavos | if (actualEvent != null) |
219 | 48686774 | Panagiotis Kanavos | { |
220 | 78ebfd2d | Panagiotis Kanavos | actualEvent(sender, e); |
221 | 48686774 | Panagiotis Kanavos | //For Folders, raise Created events for all children |
222 | 174bbb6e | Panagiotis Kanavos | RaiseCreatedForChildren(sender, e); |
223 | 48686774 | Panagiotis Kanavos | } |
224 | 78ebfd2d | Panagiotis Kanavos | } |
225 | 78ebfd2d | Panagiotis Kanavos | finally |
226 | 78ebfd2d | Panagiotis Kanavos | { |
227 | 78ebfd2d | Panagiotis Kanavos | //Finally, make sure the cached path is cleared |
228 | 78ebfd2d | Panagiotis Kanavos | _cachedDeletedFullPath = null; |
229 | 78ebfd2d | Panagiotis Kanavos | } |
230 | 78ebfd2d | Panagiotis Kanavos | } |
231 | 78ebfd2d | Panagiotis Kanavos | |
232 | 48686774 | Panagiotis Kanavos | private void RaiseCreatedForChildren(object sender, FileSystemEventArgs e) |
233 | 48686774 | Panagiotis Kanavos | { |
234 | 48686774 | Panagiotis Kanavos | Contract.Requires(sender!=null); |
235 | 48686774 | Panagiotis Kanavos | Contract.Requires(e!=null); |
236 | 48686774 | Panagiotis Kanavos | |
237 | 48686774 | Panagiotis Kanavos | if (e.ChangeType != WatcherChangeTypes.Created) |
238 | 48686774 | Panagiotis Kanavos | return; |
239 | 48686774 | Panagiotis Kanavos | var dir= new DirectoryInfo(e.FullPath); |
240 | 48686774 | Panagiotis Kanavos | //Skip if this is not a folder |
241 | 48686774 | Panagiotis Kanavos | if (!dir.Exists) |
242 | 48686774 | Panagiotis Kanavos | return; |
243 | 174bbb6e | Panagiotis Kanavos | try |
244 | 48686774 | Panagiotis Kanavos | { |
245 | 174bbb6e | Panagiotis Kanavos | foreach (var info in dir.EnumerateFileSystemInfos("*",SearchOption.AllDirectories)) |
246 | 174bbb6e | Panagiotis Kanavos | { |
247 | 174bbb6e | Panagiotis Kanavos | var path = Path.GetDirectoryName(info.FullName); |
248 | 174bbb6e | Panagiotis Kanavos | Created(sender,new FileSystemEventArgs(WatcherChangeTypes.Created,path,info.Name)); |
249 | 174bbb6e | Panagiotis Kanavos | } |
250 | 174bbb6e | Panagiotis Kanavos | } |
251 | 174bbb6e | Panagiotis Kanavos | catch (IOException exc) |
252 | 174bbb6e | Panagiotis Kanavos | { |
253 | 174bbb6e | Panagiotis Kanavos | TaskEx.Delay(1000) |
254 | 174bbb6e | Panagiotis Kanavos | .ContinueWith(_=>RaiseCreatedForChildren(sender,e)); |
255 | 174bbb6e | Panagiotis Kanavos | |
256 | 174bbb6e | Panagiotis Kanavos | } |
257 | 48686774 | Panagiotis Kanavos | } |
258 | 48686774 | Panagiotis Kanavos | |
259 | 78ebfd2d | Panagiotis Kanavos | private bool HandleMoved(object sender, FileSystemEventArgs e) |
260 | 78ebfd2d | Panagiotis Kanavos | { |
261 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
262 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
263 | 78ebfd2d | Panagiotis Kanavos | if (!(e.ChangeType == WatcherChangeTypes.Created || |
264 | 78ebfd2d | Panagiotis Kanavos | e.ChangeType == WatcherChangeTypes.Changed)) |
265 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentException("e"); |
266 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
267 | 78ebfd2d | Panagiotis Kanavos | |
268 | 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 |
269 | 78ebfd2d | Panagiotis Kanavos | //instead of a sequence of independent actions |
270 | 78ebfd2d | Panagiotis Kanavos | //One way to detect this would be to request that the full paths are NOT the same |
271 | 78ebfd2d | Panagiotis Kanavos | |
272 | 78ebfd2d | Panagiotis Kanavos | var oldName = Path.GetFileName(_cachedDeletedFullPath); |
273 | 78ebfd2d | Panagiotis Kanavos | //NOTE: e.Name is a path relative to the watched path. We MUST call Path.GetFileName to get the actual path |
274 | 78ebfd2d | Panagiotis Kanavos | var newName = Path.GetFileName(e.Name); |
275 | 78ebfd2d | Panagiotis Kanavos | //If the last deleted filename is equal to the current and the action is create, we have a MOVE operation |
276 | 78ebfd2d | Panagiotis Kanavos | var hasMoved = (_cachedDeletedFullPath != e.FullPath && oldName == newName); |
277 | 78ebfd2d | Panagiotis Kanavos | |
278 | 78ebfd2d | Panagiotis Kanavos | if (!hasMoved) |
279 | 78ebfd2d | Panagiotis Kanavos | return false; |
280 | 78ebfd2d | Panagiotis Kanavos | |
281 | 78ebfd2d | Panagiotis Kanavos | try |
282 | 78ebfd2d | Panagiotis Kanavos | { |
283 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
284 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("Moved for [{0}]", e.FullPath); |
285 | 174bbb6e | Panagiotis Kanavos | |
286 | 78ebfd2d | Panagiotis Kanavos | //If the actual action is a Move, raise a Move event instead of the actual event |
287 | 78ebfd2d | Panagiotis Kanavos | var newDirectory = Path.GetDirectoryName(e.FullPath); |
288 | 78ebfd2d | Panagiotis Kanavos | var oldDirectory = Path.GetDirectoryName(_cachedDeletedFullPath); |
289 | 139ac1e8 | Panagiotis Kanavos | |
290 | 78ebfd2d | Panagiotis Kanavos | if (Moved != null) |
291 | 139ac1e8 | Panagiotis Kanavos | { |
292 | 78ebfd2d | Panagiotis Kanavos | Moved(sender, new MovedEventArgs(newDirectory, newName, oldDirectory, oldName)); |
293 | 139ac1e8 | Panagiotis Kanavos | //If the moved item is a dictionary, we need to raise a change event for each child item |
294 | 139ac1e8 | Panagiotis Kanavos | //When a directory is moved within the same volume, Windows raises events only for the directory object, |
295 | 139ac1e8 | Panagiotis Kanavos | //not its children. This happens because the move actually changes a single directory entry. It doesn't |
296 | 139ac1e8 | Panagiotis Kanavos | //affect the entries of the children. |
297 | 139ac1e8 | Panagiotis Kanavos | var directory = new DirectoryInfo(e.FullPath); |
298 | 139ac1e8 | Panagiotis Kanavos | if (directory.Exists) |
299 | 139ac1e8 | Panagiotis Kanavos | { |
300 | 139ac1e8 | Panagiotis Kanavos | foreach (var child in directory.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)) |
301 | 139ac1e8 | Panagiotis Kanavos | { |
302 | 139ac1e8 | Panagiotis Kanavos | var newChildDirectory = Path.GetDirectoryName(child.FullName); |
303 | 139ac1e8 | Panagiotis Kanavos | |
304 | 139ac1e8 | Panagiotis Kanavos | var relativePath=child.AsRelativeTo(newDirectory); |
305 | 139ac1e8 | Panagiotis Kanavos | var relativeFolder = Path.GetDirectoryName(relativePath); |
306 | 139ac1e8 | Panagiotis Kanavos | var oldChildDirectory = Path.Combine(oldDirectory, relativeFolder); |
307 | 139ac1e8 | Panagiotis Kanavos | Moved(sender,new MovedEventArgs(newChildDirectory,child.Name,oldChildDirectory,child.Name)); |
308 | 139ac1e8 | Panagiotis Kanavos | } |
309 | 139ac1e8 | Panagiotis Kanavos | } |
310 | 139ac1e8 | Panagiotis Kanavos | |
311 | 139ac1e8 | Panagiotis Kanavos | } |
312 | 139ac1e8 | Panagiotis Kanavos | |
313 | 78ebfd2d | Panagiotis Kanavos | } |
314 | 78ebfd2d | Panagiotis Kanavos | finally |
315 | 78ebfd2d | Panagiotis Kanavos | { |
316 | 78ebfd2d | Panagiotis Kanavos | _cachedDeletedFullPath = null; |
317 | 78ebfd2d | Panagiotis Kanavos | } |
318 | 78ebfd2d | Panagiotis Kanavos | return true; |
319 | 78ebfd2d | Panagiotis Kanavos | } |
320 | 78ebfd2d | Panagiotis Kanavos | |
321 | 78ebfd2d | Panagiotis Kanavos | private void PropagateCachedDeleted(object sender) |
322 | 78ebfd2d | Panagiotis Kanavos | { |
323 | 78ebfd2d | Panagiotis Kanavos | if (sender == null) |
324 | 78ebfd2d | Panagiotis Kanavos | throw new ArgumentNullException("sender"); |
325 | 78ebfd2d | Panagiotis Kanavos | Contract.Ensures(_cachedDeletedFullPath == null); |
326 | 78ebfd2d | Panagiotis Kanavos | Contract.EndContractBlock(); |
327 | 78ebfd2d | Panagiotis Kanavos | |
328 | 78ebfd2d | Panagiotis Kanavos | //Nothing to handle if there is no cached deleted file |
329 | 78ebfd2d | Panagiotis Kanavos | if (String.IsNullOrWhiteSpace(_cachedDeletedFullPath)) |
330 | 78ebfd2d | Panagiotis Kanavos | return; |
331 | 78ebfd2d | Panagiotis Kanavos | |
332 | 78ebfd2d | Panagiotis Kanavos | var deletedFileName = Path.GetFileName(_cachedDeletedFullPath); |
333 | 78ebfd2d | Panagiotis Kanavos | var deletedFileDirectory = Path.GetDirectoryName(_cachedDeletedFullPath); |
334 | 78ebfd2d | Panagiotis Kanavos | |
335 | 174bbb6e | Panagiotis Kanavos | if (Log.IsDebugEnabled) |
336 | 174bbb6e | Panagiotis Kanavos | Log.DebugFormat("Propagating delete for [{0}]", _cachedDeletedFullPath); |
337 | 174bbb6e | Panagiotis Kanavos | |
338 | 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 |
339 | 139ac1e8 | Panagiotis Kanavos | //In this case we need to raise the proper events for all child objects of the deleted directory. |
340 | 139ac1e8 | Panagiotis Kanavos | //UNFORTUNATELY, this can't be detected here, eg. by retrieving the child objects, because they are already deleted |
341 | 139ac1e8 | Panagiotis Kanavos | //This should be done at a higher level, eg by checking the stored state |
342 | 139ac1e8 | Panagiotis Kanavos | if (Deleted != null) |
343 | 139ac1e8 | Panagiotis Kanavos | Deleted(sender,new FileSystemEventArgs(WatcherChangeTypes.Deleted, deletedFileDirectory, deletedFileName)); |
344 | 78ebfd2d | Panagiotis Kanavos | |
345 | 78ebfd2d | Panagiotis Kanavos | _cachedDeletedFullPath = null; |
346 | 78ebfd2d | Panagiotis Kanavos | } |
347 | 78ebfd2d | Panagiotis Kanavos | } |
348 | 78ebfd2d | Panagiotis Kanavos | } |