Statistics
| Branch: | Revision:

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
}