Statistics
| Branch: | Revision:

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
}