This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / System / System.IO / DefaultWatcher.cs
1 // 
2 // System.IO.DefaultWatcher.cs: default IFileWatcher
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (c) 2004 Novell, Inc. (http://www.novell.com)
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Collections;
33 using System.IO;
34 using System.Threading;
35
36 namespace System.IO {
37         class DefaultWatcherData {
38                 public FileSystemWatcher FSW;
39                 public string Directory;
40                 public string FileMask;
41                 public bool IncludeSubdirs;
42                 public bool Enabled;
43                 public DateTime DisabledTime;
44                 public Hashtable Files;
45         }
46
47         class FileData {
48                 public string Directory;
49                 public FileAttributes Attributes;
50                 public bool NotExists;
51                 public DateTime CreationTime;
52                 public DateTime LastWriteTime;
53         }
54
55         class DefaultWatcher : IFileWatcher
56         {
57                 static DefaultWatcher instance;
58                 static Thread thread;
59                 static Hashtable watches;
60
61                 private DefaultWatcher ()
62                 {
63                 }
64                 
65                 public static bool GetInstance (out IFileWatcher watcher)
66                 {
67                         lock (typeof (DefaultWatcher)) {
68                                 if (instance != null) {
69                                         watcher = instance;
70                                         return true;
71                                 }
72
73                                 instance = new DefaultWatcher ();
74                                 watcher = instance;
75                                 return true;
76                         }
77                 }
78                 
79                 public void StartDispatching (FileSystemWatcher fsw)
80                 {
81                         DefaultWatcherData data;
82                         lock (this) {
83                                 if (watches == null)
84                                         watches = new Hashtable ();
85
86                                 if (thread == null) {
87                                         thread = new Thread (new ThreadStart (Monitor));
88                                         thread.IsBackground = true;
89                                         thread.Start ();
90                                 }
91                         }
92
93                         lock (watches) {
94                                 data = (DefaultWatcherData) watches [fsw];
95                                 if (data == null) {
96                                         data = new DefaultWatcherData ();
97                                         data.Files = new Hashtable ();
98                                         watches [fsw] = data;
99                                 }
100
101                                 data.FSW = fsw;
102                                 data.Directory = fsw.FullPath;
103                                 data.FileMask = fsw.MangledFilter;
104                                 data.IncludeSubdirs = fsw.IncludeSubdirectories;
105                                 data.Enabled = true;
106                                 data.DisabledTime = DateTime.MaxValue;
107                                 UpdateDataAndDispatch (data, false);
108                         }
109                 }
110
111                 public void StopDispatching (FileSystemWatcher fsw)
112                 {
113                         DefaultWatcherData data;
114                         lock (watches) {
115                                 data = (DefaultWatcherData) watches [fsw];
116                                 if (data != null) {
117                                         data.Enabled = false;
118                                         data.DisabledTime = DateTime.Now;
119                                 }
120                         }
121                 }
122
123
124                 void Monitor ()
125                 {
126                         int zeroes = 0;
127
128                         while (true) {
129                                 lock (watches) {
130                                         if (watches.Count > 0) {
131                                                 zeroes = 0;
132                                                 ArrayList removed = null;
133                                                 foreach (DefaultWatcherData data in watches.Values) {
134                                                         bool remove = UpdateDataAndDispatch (data, true);
135                                                         if (remove) {
136                                                                 if (removed == null)
137                                                                         removed = new ArrayList ();
138
139                                                                 removed.Add (data);
140                                                         }
141                                                 }
142
143                                                 if (removed != null) {
144                                                         foreach (DefaultWatcherData data in removed)
145                                                                 watches.Remove (data.FSW);
146
147                                                         removed.Clear ();
148                                                         removed = null;
149                                                 }
150                                         } else {
151                                                 zeroes++;
152                                                 if (zeroes == 20)
153                                                         break;
154                                         }
155                                 }
156                                 Thread.Sleep (750);
157                         }
158
159                         lock (this) {
160                                 thread = null;
161                         }
162                 }
163                 
164                 bool UpdateDataAndDispatch (DefaultWatcherData data, bool dispatch)
165                 {
166                         if (!data.Enabled) {
167                                 return (data.DisabledTime != DateTime.MaxValue &&
168                                         (DateTime.Now - data.DisabledTime).TotalSeconds > 5);
169                         }
170
171                         DoFiles (data, data.Directory, dispatch);
172                         return false;
173                 }
174
175                 static void DispatchEvents (FileSystemWatcher fsw, FileAction action, string filename)
176                 {
177                         RenamedEventArgs renamed = null;
178
179                         lock (fsw) {
180                                 fsw.DispatchEvents (action, filename, ref renamed);
181                                 if (fsw.Waiting) {
182                                         fsw.Waiting = false;
183                                         System.Threading.Monitor.PulseAll (fsw);
184                                 }
185                         }
186                 }
187
188                 void DoFiles (DefaultWatcherData data, string directory, bool dispatch)
189                 {
190                         bool direxists = Directory.Exists (directory);
191                         if (direxists && data.IncludeSubdirs) {
192                                 foreach (string d in Directory.GetDirectories (directory))
193                                         DoFiles (data, d, dispatch);
194                         }
195
196                         string [] files = null;
197                         if (direxists) {
198                                 files = Directory.GetFileSystemEntries (directory, data.FileMask);
199                         } else {
200                                 files = new string [0];
201                         }
202
203                         /* Set all as untested */
204                         foreach (string filename in data.Files.Keys) {
205                                 FileData fd = (FileData) data.Files [filename];
206                                 if (fd.Directory == directory)
207                                         fd.NotExists = true;
208                         }
209
210                         /* New files */
211                         foreach (string filename in files) {
212                                 FileData fd = (FileData) data.Files [filename];
213                                 if (fd == null) {
214                                         data.Files.Add (filename, CreateFileData (directory, filename));
215                                         if (dispatch)
216                                                 DispatchEvents (data.FSW, FileAction.Added, filename);
217                                 } else if (fd.Directory == directory) {
218                                         fd.NotExists = false;
219                                 }
220                         }
221
222                         if (!dispatch) // We only initialize the file list
223                                 return;
224
225                         /* Removed files */
226                         ArrayList removed = null;
227                         foreach (string filename in data.Files.Keys) {
228                                 FileData fd = (FileData) data.Files [filename];
229                                 if (fd.NotExists) {
230                                         if (removed == null)
231                                                 removed = new ArrayList ();
232
233                                         removed.Add (filename);
234                                         DispatchEvents (data.FSW, FileAction.Removed, filename);
235                                 }
236                         }
237
238                         if (removed != null) {
239                                 foreach (string filename in removed)
240                                         data.Files.Remove (filename);
241
242                                 removed = null;
243                         }
244
245                         /* Changed files */
246                         foreach (string filename in data.Files.Keys) {
247                                 FileData fd = (FileData) data.Files [filename];
248                                 DateTime creation, write;
249                                 try {
250                                         creation = File.GetCreationTime (filename);
251                                         write = File.GetLastWriteTime (filename);
252                                 } catch {
253                                         /* Deleted */
254                                         if (removed == null)
255                                                 removed = new ArrayList ();
256
257                                         removed.Add (filename);
258                                         DispatchEvents (data.FSW, FileAction.Removed, filename);
259                                         continue;
260                                 }
261                                 
262                                 if (creation != fd.CreationTime || write != fd.LastWriteTime) {
263                                         fd.CreationTime = creation;
264                                         fd.LastWriteTime = write;
265                                         DispatchEvents (data.FSW, FileAction.Modified, filename);
266                                 }
267                         }
268
269                         if (removed != null) {
270                                 foreach (string filename in removed)
271                                         data.Files.Remove (filename);
272                         }
273
274                 }
275
276                 static FileData CreateFileData (string directory, string filename)
277                 {
278                         FileData fd = new FileData ();
279                         string fullpath = Path.Combine (directory, filename);
280                         fd.Directory = directory;
281                         fd.Attributes = File.GetAttributes (fullpath);
282                         fd.CreationTime = File.GetCreationTime (fullpath);
283                         fd.LastWriteTime = File.GetLastWriteTime (fullpath);
284                         return fd;
285                 }
286         }
287 }
288