2004-01-16 Gonzalo Paniagua Javier <gonzalo@ximian.com>
authorGonzalo Paniagua Javier <gonzalo.mono@gmail.com>
Fri, 16 Jan 2004 03:27:24 +0000 (03:27 -0000)
committerGonzalo Paniagua Javier <gonzalo.mono@gmail.com>
Fri, 16 Jan 2004 03:27:24 +0000 (03:27 -0000)
* System.dll.sources: added new files.

* System_test.dll.sources: added new test.

* System.IO/DefaultWatcher.cs: stub for the default watcher.
* System.IO/WindowsWatcher.cs: stub for the windows watcher.

* System.IO/FAMWatcher.cs: FAM watcher.

* System.IO/FileAction.cs: enum with event types.

* System.IO/FileSystemEventArgs.cs: added SetName property.
* System.IO/FileSystemWatcher.cs: added support for the 3 watchers.

* System.IO/IFileWatcher.cs: interface implemented by the watchers.
* System.IO/SearchPattern.cs: copied from corlib.
* Test/System.IO/FileSystemWatcherTest.cs: new test.

svn path=/trunk/mcs/; revision=22139

14 files changed:
mcs/class/System/ChangeLog
mcs/class/System/System.IO/ChangeLog
mcs/class/System/System.IO/DefaultWatcher.cs [new file with mode: 0644]
mcs/class/System/System.IO/FAMWatcher.cs [new file with mode: 0644]
mcs/class/System/System.IO/FileAction.cs [new file with mode: 0644]
mcs/class/System/System.IO/FileSystemEventArgs.cs
mcs/class/System/System.IO/FileSystemWatcher.cs
mcs/class/System/System.IO/IFileWatcher.cs [new file with mode: 0644]
mcs/class/System/System.IO/SearchPattern.cs [new file with mode: 0644]
mcs/class/System/System.IO/WindowsWatcher.cs [new file with mode: 0644]
mcs/class/System/System.dll.sources
mcs/class/System/System_test.dll.sources
mcs/class/System/Test/System.IO/ChangeLog [new file with mode: 0644]
mcs/class/System/Test/System.IO/FileSystemWatcherTest.cs [new file with mode: 0755]

index 20be7ebe36e5510648a3c79af7864a0b312efa48..41d9a86e1554b2ee28dcfef5eff7d6c39d1c4f59 100644 (file)
@@ -1,3 +1,9 @@
+2004-01-16  Gonzalo Paniagua Javier <gonzalo@ximian.com>
+
+       * System.dll.sources: added new files.
+
+       * System_test.dll.sources: added new test.
+
 2004-01-01  Nick Drochak <ndrochak@gol.com>
 
        * Makefile: Suppress warnings about multiple Regex defs and obsolete
index 7364988b9b3e33026629a27ec084946c74b69721..ab893886e2a6fe60bdd8c123f609c42814ead1ed 100755 (executable)
@@ -1,6 +1,22 @@
+2004-01-16  Gonzalo Paniagua Javier <gonzalo@ximian.com>
+
+       * DefaultWatcher.cs: stub for the default watcher.
+       * WindowsWatcher.cs: stub for the windows watcher.
+       
+       * FAMWatcher.cs: FAM watcher.
+       
+       * FileAction.cs: enum with event types.
+       
+       * FileSystemEventArgs.cs: added SetName property.
+       * FileSystemWatcher.cs: added support for the 3 watchers.
+
+       * IFileWatcher.cs: interface implemented by the watchers. 
+       * SearchPattern.cs: copied from corlib.
+
 2003-07-17  Andreas Nahr <ClassDevelopment@A-SoftTech.com>
 
-       * FileSystemWatcher.cs: Reworked attributes based on the new Consts scheme
+       * FileSystemWatcher.cs: Reworked attributes based on the new Consts
+       scheme
 
 2003-07-13  Andreas Nahr <ClassDevelopment@A-SoftTech.com>
 
diff --git a/mcs/class/System/System.IO/DefaultWatcher.cs b/mcs/class/System/System.IO/DefaultWatcher.cs
new file mode 100644 (file)
index 0000000..b3bca51
--- /dev/null
@@ -0,0 +1,31 @@
+// 
+// System.IO.DefaultWatcher.cs: windows IFileWatcher
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+namespace System.IO {
+       class DefaultWatcher : IFileWatcher
+       {
+               private DefaultWatcher ()
+               {
+               }
+               
+               public static bool GetInstance (out IFileWatcher watcher)
+               {
+                       throw new NotSupportedException ();
+               }
+               
+               public void StartDispatching (FileSystemWatcher fsw)
+               {
+               }
+
+               public void StopDispatching (FileSystemWatcher fsw)
+               {
+               }
+       }
+}
+
diff --git a/mcs/class/System/System.IO/FAMWatcher.cs b/mcs/class/System/System.IO/FAMWatcher.cs
new file mode 100644 (file)
index 0000000..1128787
--- /dev/null
@@ -0,0 +1,263 @@
+// 
+// System.IO.FAM.cs: interface with libfam
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace System.IO {
+       struct FAMConnection {
+               public int FD;
+               IntPtr opaque;
+       }
+
+       struct FAMRequest {
+               public int ReqNum;
+       }
+
+       enum FAMCodes {
+               Changed = 1,
+               Deleted = 2,
+               StartExecuting = 3, 
+               StopExecuting = 4,
+               Created = 5,
+               Moved = 6, 
+               Acknowledge = 7,
+               Exists = 8,
+               EndExist = 9
+       };
+
+       class FAMData {
+               public FileSystemWatcher FSW;
+               public string Directory;
+               public string FileMask;
+               public bool IncludeSubdirs;
+               public bool Enabled;
+               public FAMRequest Request;
+       }
+
+       class FAMWatcher : IFileWatcher
+       {
+               static bool failed;
+               static FAMWatcher instance;
+               static Hashtable watches;
+               static Hashtable requests;
+               static FAMConnection conn;
+               static Thread thread;
+               static bool stop;
+               
+               private FAMWatcher ()
+               {
+               }
+               
+               public static bool GetInstance (out IFileWatcher watcher)
+               {
+                       lock (typeof (FAMWatcher)) {
+                               if (failed == true) {
+                                       watcher = null;
+                                       return false;
+                               }
+
+                               if (instance != null) {
+                                       watcher = instance;
+                                       return true;
+                               }
+
+                               watches = new Hashtable ();
+                               requests = new Hashtable ();
+                               if (FAMOpen (out conn) == -1) {
+                                       failed = true;
+                                       watcher = null;
+                                       return false;
+                               }
+
+                               instance = new FAMWatcher ();
+                               watcher = instance;
+                               return true;
+                       }
+               }
+               
+               public void StartDispatching (FileSystemWatcher fsw)
+               {
+                       FAMData data;
+                       lock (this) {
+                               if (thread == null) {
+                                       thread = new Thread (new ThreadStart (Monitor));
+                                       thread.IsBackground = true;
+                                       thread.Start ();
+                               }
+
+                               data = (FAMData) watches [fsw];
+                       }
+
+                       if (data == null) {
+                               data = new FAMData ();
+                               data.FSW = fsw;
+                               data.Directory = fsw.FullPath;
+                               data.FileMask = fsw.Filter;
+                               data.IncludeSubdirs = fsw.IncludeSubdirectories;
+                               data.Enabled = true;
+                               lock (this) {
+                                       StartMonitoringDirectory (data);
+                                       watches [fsw] = data;
+                                       requests [data.Request.ReqNum] = data;
+                               }
+                       }
+               }
+
+               static void StartMonitoringDirectory (FAMData data)
+               {
+                       FAMRequest fr;
+                       if (FAMMonitorDirectory (ref conn, data.Directory, out fr, IntPtr.Zero) == -1)
+                               throw new Win32Exception ();
+
+                       data.Request = fr;
+               }
+
+               public void StopDispatching (FileSystemWatcher fsw)
+               {
+                       FAMData data;
+                       lock (this) {
+                               data = (FAMData) watches [fsw];
+                               if (data == null)
+                                       return;
+
+                               StopMonitoringDirectory (data);
+                               watches.Remove (fsw);
+                               requests.Remove (data.Request.ReqNum);
+                               if (watches.Count == 0)
+                                       stop = true;
+                       }
+               }
+
+               static void StopMonitoringDirectory (FAMData data)
+               {
+                       FAMRequest fr;
+                       if (FAMCancelMonitor (ref conn, ref data.Request) == -1)
+                               throw new Win32Exception ();
+               }
+
+               void Monitor ()
+               {
+                       while (!stop) {
+                               int haveEvents;
+                               lock (this) {
+                                       haveEvents = FAMPending (ref conn);
+                               }
+
+                               if (haveEvents > 0) {
+                                       ProcessEvents ();
+                               } else {
+                                       Thread.Sleep (500);
+                               }
+                       }
+
+                       lock (this) {
+                               thread = null;
+                               stop = false;
+                       }
+               }
+
+               const NotifyFilters changed =   NotifyFilters.Attributes |
+                                               NotifyFilters.LastAccess |
+                                               NotifyFilters.Size      |
+                                               NotifyFilters.LastWrite;
+
+               void ProcessEvents ()
+               {
+                       lock (this) {
+                               do {
+                                       int code;
+                                       string filename;
+                                       int requestNumber;
+                                       FileSystemWatcher fsw;
+
+                                       if (InternalFAMNextEvent (ref conn, out filename,
+                                                                 out code, out requestNumber) != 1)
+                                               return;
+
+                                       bool found = false;
+                                       switch ((FAMCodes) code) {
+                                       case FAMCodes.Changed:
+                                       case FAMCodes.Deleted:
+                                       case FAMCodes.Created:
+                                               found = requests.ContainsKey (requestNumber);
+                                               break;
+                                       case FAMCodes.Moved:
+                                       case FAMCodes.StartExecuting:
+                                       case FAMCodes.StopExecuting:
+                                       case FAMCodes.Acknowledge:
+                                       case FAMCodes.Exists:
+                                       case FAMCodes.EndExist:
+                                       default:
+                                               found = false;
+                                               break;
+                                       }
+
+                                       if (!found)
+                                               continue;
+                                       
+                                       FAMData data = (FAMData) requests [requestNumber];
+                                       if (!data.Enabled)
+                                               continue;
+
+                                       fsw = data.FSW;
+                                       NotifyFilters flt = fsw.NotifyFilter;
+                                       RenamedEventArgs renamed = null;
+                                       FileAction fa = 0;
+                                       if (code == (int) FAMCodes.Changed && (flt & changed) != 0)
+                                               fa = FileAction.Modified;
+                                       else if (code == (int) FAMCodes.Deleted)
+                                               fa = FileAction.Removed;
+                                       else if (code == (int) FAMCodes.Created)
+                                               fa = FileAction.Added;
+                                       
+                                       if (fa != 0) {
+                                               if (filename != data.Directory && !fsw.Pattern.IsMatch (filename))
+                                                       continue;
+
+                                               lock (fsw) {
+                                                       fsw.DispatchEvents (fa, filename, ref renamed);
+                                                       if (fsw.Waiting) {
+                                                               fsw.Waiting = false;
+                                                               System.Threading.Monitor.PulseAll (fsw);
+                                                       }
+                                               }
+                                       }
+
+                               } while (FAMPending (ref conn) > 0);
+                       }
+               }
+               
+               [DllImport ("fam")]
+               extern static int FAMOpen (out FAMConnection fc);
+
+               [DllImport ("fam")]
+               extern static int FAMClose (ref FAMConnection fc);
+
+               [DllImport ("fam")]
+               extern static int FAMMonitorDirectory (ref FAMConnection fc, string filename,
+                                                       out FAMRequest fr, IntPtr user_data);
+
+               [DllImport ("fam")]
+               extern static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename,
+                                                       out int code, out int reqnum);
+
+               [DllImport ("fam")]
+               extern static int FAMPending (ref FAMConnection fc);
+       }
+}
+
diff --git a/mcs/class/System/System.IO/FileAction.cs b/mcs/class/System/System.IO/FileAction.cs
new file mode 100644 (file)
index 0000000..d5827cf
--- /dev/null
@@ -0,0 +1,18 @@
+// 
+// System.IO.FileAction.cs
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+namespace System.IO {
+       enum FileAction {
+               Added = 1,
+               Removed = 2,
+               Modified = 3,
+               RenamedOldName = 4,
+               RenamedNewName = 5
+       }
+}
index 924ed7e1c2a7906660da1bb881f6fe2ec4c1ed6d..05ddecfbeb65a619e232bade53105e44219b5011 100644 (file)
@@ -29,6 +29,10 @@ namespace System.IO {
                        this.name = name;\r
                }\r
                \r
+               internal void SetName (string name)\r
+               {\r
+                       this.name = name;\r
+               }\r
                #endregion // Constructors\r
 \r
                #region Properties\r
index 6f948be421ba13d7335108a3e6f89fb3bb8f7eec..f4d12e7e774215dc3f10d2d844232d02f998b197 100644 (file)
@@ -7,10 +7,13 @@
 //
 // Copyright (C) Tim Coleman, 2002 
 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
+// (c) 2004 Novell, Inc. (http://www.novell.com)
 //
 
 using System;
 using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Threading;
 
 namespace System.IO {
@@ -25,8 +28,13 @@ namespace System.IO {
                int internalBufferSize;
                NotifyFilters notifyFilter;
                string path;
-               ISite site;
+               string fullpath;
                ISynchronizeInvoke synchronizingObject;
+               bool disposed;
+               WaitForChangedResult lastData;
+               bool waiting;
+               SearchPattern2 pattern;
+               static IFileWatcher watcher;
 
                #endregion // Fields
 
@@ -40,10 +48,11 @@ namespace System.IO {
                        this.includeSubdirectories = false;
                        this.internalBufferSize = 8192;
                        this.path = "";
+                       InitWatcher ();
                }
 
                public FileSystemWatcher (string path)
-                       : this (path, String.Empty)
+                       : this (path, "*.*")
                {
                }
 
@@ -68,17 +77,78 @@ namespace System.IO {
                        this.notifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
                        this.path = path;
                        this.synchronizingObject = null;
+                       InitWatcher ();
+               }
+
+               void InitWatcher ()
+               {
+                       lock (typeof (FileSystemWatcher)) {
+                               if (watcher != null)
+                                       return;
+
+                               int mode = InternalSupportsFSW ();
+                               bool ok = false;
+                               if (mode == 2)
+                                       ok = FAMWatcher.GetInstance (out watcher);
+                               else if (mode == 1)
+                                       ok = WindowsWatcher.GetInstance (out watcher);
+
+                               if (mode == 0 || !ok)
+                                       DefaultWatcher.GetInstance (out watcher);
+                       }
                }
 
                #endregion // Constructors
 
                #region Properties
 
+               /* If this is enabled, we Pulse this instance */
+               internal bool Waiting {
+                       get { return waiting; }
+                       set { waiting = value; }
+               }
+
+               internal SearchPattern2 Pattern {
+                       get {
+                               if (pattern == null) {
+                                       string f = Filter;
+                                       if (f == "*.*" && !(watcher.GetType () == typeof (WindowsWatcher)))
+                                               f = "*";
+
+                                       pattern = new SearchPattern2 (f);
+                               }
+                               return pattern;
+                       }
+               }
+
+               internal string FullPath {
+                       get {
+                               if (fullpath == null) {
+                                       if (path == null || path == "")
+                                               fullpath = Environment.CurrentDirectory;
+                                       else
+                                               fullpath = System.IO.Path.GetFullPath (path);
+                               }
+
+                               return fullpath;
+                       }
+               }
+
                [DefaultValue(false)]
                [IODescription("Flag to indicate if this instance is active")]
                public bool EnableRaisingEvents {
                        get { return enableRaisingEvents; }
-                       set { enableRaisingEvents = value; }
+                       set {
+                               if (value == enableRaisingEvents)
+                                       return; // Do nothing
+
+                               enableRaisingEvents = value;
+                               if (value) {
+                                       Start ();
+                               } else {
+                                       Stop ();
+                               }
+                       }
                }
 
                [DefaultValue("*.*")]
@@ -88,9 +158,13 @@ namespace System.IO {
                public string Filter {
                        get { return filter; }
                        set {
-                               filter = value;
-                               if (filter == null || filter == "")
-                                       filter = "*.*";
+                               if (value == null || value == "")
+                                       value = "*.*";
+
+                               if (filter != value) {
+                                       filter = value;
+                                       pattern = null;
+                               }
                        }
                }
 
@@ -98,22 +172,51 @@ namespace System.IO {
                [IODescription("Flag to indicate we want to watch subdirectories")]
                public bool IncludeSubdirectories {
                        get { return includeSubdirectories; }
-                       set { includeSubdirectories = value; }
+                       set {
+                               if (includeSubdirectories == value)
+                                       return;
+
+                               includeSubdirectories = value;
+                               if (value && enableRaisingEvents) {
+                                       Stop ();
+                                       Start ();
+                               }
+                       }
                }
 
                [Browsable(false)]
                [DefaultValue(8192)]
                public int InternalBufferSize {
                        get { return internalBufferSize; }
-                       set { internalBufferSize = value; }
+                       set {
+                               if (internalBufferSize == value)
+                                       return;
+
+                               if (value < 4196)
+                                       value = 4196;
+
+                               internalBufferSize = value;
+                               if (enableRaisingEvents) {
+                                       Stop ();
+                                       Start ();
+                               }
+                       }
                }
 
                [DefaultValue(NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite)]
                [IODescription("Flag to indicate which change event we want to monitor")]
                public NotifyFilters NotifyFilter {
                        get { return notifyFilter; }
-                       [MonoTODO ("Perform validation.")]
-                       set { notifyFilter = value; }
+                       set {
+                               if (notifyFilter == value)
+                                       return;
+                                       
+                               notifyFilter = value;
+                               if (enableRaisingEvents) {
+                                       Stop ();
+                                       Start ();
+                               }
+                       }
                }
 
                [DefaultValue("")]
@@ -124,13 +227,15 @@ namespace System.IO {
                public string Path {
                        get { return path; }
                        set {
+                               if (path == value)
+                                       return;
+
                                bool exists = false;
                                Exception exc = null;
 
                                try {
                                        exists = Directory.Exists (value);
                                } catch (Exception e) {
-                                       exists = false;
                                        exc = e;
                                }
 
@@ -141,13 +246,18 @@ namespace System.IO {
                                        throw new ArgumentException ("Directory does not exists", "value");
 
                                path = value;
+                               fullpath = null;
+                               if (enableRaisingEvents) {
+                                       Stop ();
+                                       Start ();
+                               }
                        }
                }
 
                [Browsable(false)]
                public override ISite Site {
-                       get { return site; }
-                       set { site = value; }
+                       get { return base.Site; }
+                       set { base.Site = value; }
                }
 
                [DefaultValue(null)]
@@ -167,12 +277,12 @@ namespace System.IO {
                        throw new NotImplementedException (); 
                }
 
-               [MonoTODO]
                protected override void Dispose (bool disposing)
                {
                        if (disposing) {
-                               // 
+                               Stop ();
                        }
+                       disposed = true;
                        base.Dispose (disposing);
                }
 
@@ -227,12 +337,83 @@ namespace System.IO {
                        return WaitForChanged (changeType, Timeout.Infinite);
                }
 
-               [MonoTODO]
                public WaitForChangedResult WaitForChanged (WatcherChangeTypes changeType, int timeout)
                {
-                       throw new NotImplementedException (); 
+                       WaitForChangedResult result = new WaitForChangedResult ();
+                       bool prevEnabled = EnableRaisingEvents;
+                       if (!prevEnabled)
+                               EnableRaisingEvents = true;
+
+                       bool gotData;
+                       lock (this) {
+                               waiting = true;
+                               gotData = Monitor.Wait (this, timeout);
+                               if (gotData)
+                                       result = this.lastData;
+                       }
+
+                       EnableRaisingEvents = prevEnabled;
+                       if (!gotData)
+                               result.TimedOut = true;
+
+                       return result;
                }
 
+               internal void DispatchEvents (FileAction act, string filename, ref RenamedEventArgs renamed)
+               {
+                       if (waiting) {
+                               lastData = new WaitForChangedResult ();
+                       }
+
+                       switch (act) {
+                       case FileAction.Added:
+                               lastData.Name = filename;
+                               lastData.ChangeType = WatcherChangeTypes.Created;
+                               OnCreated (new FileSystemEventArgs (WatcherChangeTypes.Created, path, filename));
+                               break;
+                       case FileAction.Removed:
+                               lastData.Name = filename;
+                               lastData.ChangeType = WatcherChangeTypes.Deleted;
+                               OnDeleted (new FileSystemEventArgs (WatcherChangeTypes.Deleted, path, filename));
+                               break;
+                       case FileAction.Modified:
+                               lastData.Name = filename;
+                               lastData.ChangeType = WatcherChangeTypes.Changed;
+                               OnChanged (new FileSystemEventArgs (WatcherChangeTypes.Changed, path, filename));
+                               break;
+                       case FileAction.RenamedOldName:
+                               if (renamed != null) {
+                                       OnRenamed (renamed);
+                               }
+                               lastData.OldName = filename;
+                               lastData.ChangeType = WatcherChangeTypes.Renamed;
+                               renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, null, filename);
+                               break;
+                       case FileAction.RenamedNewName:
+                               lastData.Name = filename;
+                               lastData.ChangeType = WatcherChangeTypes.Renamed;
+                               if (renamed != null) {
+                                       renamed.SetName (filename);
+                               } else {
+                                       renamed = new RenamedEventArgs (WatcherChangeTypes.Renamed, path, filename, null);
+                               }
+                               OnRenamed (renamed);
+                               renamed = null;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               void Start ()
+               {
+                       watcher.StartDispatching (this);
+               }
+
+               void Stop ()
+               {
+                       watcher.StopDispatching (this);
+               }
                #endregion // Methods
 
                #region Events and Delegates
@@ -254,5 +435,27 @@ namespace System.IO {
                public event RenamedEventHandler Renamed;
 
                #endregion // Events and Delegates
+
+               /* 0 -> not supported   */
+               /* 1 -> windows         */
+               /* 2 -> FAM             */
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern int InternalSupportsFSW ();
+               
+               /*[MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern IntPtr InternalOpenDirectory (string path, IntPtr reserved);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern IntPtr InternalCloseDirectory (IntPtr handle);
+
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               static extern bool InternalReadDirectoryChanges (IntPtr handle,
+                                                                byte [] buffer,
+                                                                bool includeSubdirectories,
+                                                                NotifyFilters notifyFilter,
+                                                                out NativeOverlapped overlap,
+                                                                OverlappedHandler overlappedCallback);
+
+               */
        }
 }
diff --git a/mcs/class/System/System.IO/IFileWatcher.cs b/mcs/class/System/System.IO/IFileWatcher.cs
new file mode 100644 (file)
index 0000000..6b9bf6a
--- /dev/null
@@ -0,0 +1,16 @@
+// 
+// System.IO.IFileWatcher.cs
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+namespace System.IO {
+       interface IFileWatcher {
+               void StartDispatching (FileSystemWatcher fsw);
+               void StopDispatching (FileSystemWatcher fsw);
+       }
+}
+
diff --git a/mcs/class/System/System.IO/SearchPattern.cs b/mcs/class/System/System.IO/SearchPattern.cs
new file mode 100644 (file)
index 0000000..b2bd783
--- /dev/null
@@ -0,0 +1,170 @@
+//
+// System.IO.SearchPattern2.cs: Filename glob support.
+//
+// Author:
+//   Dan Lewis (dihlewis@yahoo.co.uk)
+//
+// (C) 2002
+//
+
+// Copied from corlib/System.IO/SearchPatter.cs
+using System;
+
+namespace System.IO {
+
+       // FIXME: there's a complication with this algorithm under windows.
+       // the pattern '*.*' matches all files (i think . matches the extension),
+       // whereas under UNIX it should only match files containing the '.' character.
+
+       class SearchPattern2 {
+               public SearchPattern2 (string pattern) : this (pattern, false) { }
+
+               public SearchPattern2 (string pattern, bool ignore)
+               {
+                       this.ignore = ignore;
+                       Compile (pattern);
+               }
+
+               public bool IsMatch (string text)
+               {
+                       return Match (ops, text, 0);
+               }
+
+               // private
+
+               private Op ops;         // the compiled pattern
+               private bool ignore;    // ignore case
+
+               private void Compile (string pattern)
+               {
+                       if (pattern == null || pattern.IndexOfAny (InvalidChars) >= 0)
+                               throw new ArgumentException ("Invalid search pattern.");
+
+                       if (pattern == "*") {   // common case
+                               ops = new Op (OpCode.True);
+                               return;
+                       }
+
+                       ops = null;
+
+                       int ptr = 0;
+                       Op last_op = null;
+                       while (ptr < pattern.Length) {
+                               Op op;
+                       
+                               switch (pattern [ptr]) {
+                               case '?':
+                                       op = new Op (OpCode.AnyChar);
+                                       ++ ptr;
+                                       break;
+
+                               case '*':
+                                       op = new Op (OpCode.AnyString);
+                                       ++ ptr;
+                                       break;
+                                       
+                               default:
+                                       op = new Op (OpCode.ExactString);
+                                       int end = pattern.IndexOfAny (WildcardChars, ptr);
+                                       if (end < 0)
+                                               end = pattern.Length;
+
+                                       op.Argument = pattern.Substring (ptr, end - ptr);
+                                       if (ignore)
+                                               op.Argument = op.Argument.ToLower ();
+
+                                       ptr = end;
+                                       break;
+                               }
+
+                               if (last_op == null)
+                                       ops = op;
+                               else
+                                       last_op.Next = op;
+
+                               last_op = op;
+                       }
+
+                       if (last_op == null)
+                               ops = new Op (OpCode.End);
+                       else
+                               last_op.Next = new Op (OpCode.End);
+               }
+
+               private bool Match (Op op, string text, int ptr)
+               {
+                       while (op != null) {
+                               switch (op.Code) {
+                               case OpCode.True:
+                                       return true;
+
+                               case OpCode.End:
+                                       if (ptr == text.Length)
+                                               return true;
+
+                                       return false;
+                               
+                               case OpCode.ExactString:
+                                       int length = op.Argument.Length;
+                                       if (ptr + length > text.Length)
+                                               return false;
+
+                                       string str = text.Substring (ptr, length);
+                                       if (ignore)
+                                               str = str.ToLower ();
+
+                                       if (str != op.Argument)
+                                               return false;
+
+                                       ptr += length;
+                                       break;
+
+                               case OpCode.AnyChar:
+                                       if (++ ptr > text.Length)
+                                               return false;
+                                       break;
+
+                               case OpCode.AnyString:
+                                       while (ptr <= text.Length) {
+                                               if (Match (op.Next, text, ptr))
+                                                       return true;
+
+                                               ++ ptr;
+                                       }
+
+                                       return false;
+                               }
+
+                               op = op.Next;
+                       }
+
+                       return true;
+               }
+
+               // private static
+
+               internal static readonly char [] WildcardChars = { '*', '?' };
+               internal static readonly char [] InvalidChars = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
+
+               private class Op {
+                       public Op (OpCode code)
+                       {
+                               this.Code = code;
+                               this.Argument = null;
+                               this.Next = null;
+                       }
+               
+                       public OpCode Code;
+                       public string Argument;
+                       public Op Next;
+               }
+
+               private enum OpCode {
+                       ExactString,            // literal
+                       AnyChar,                // ?
+                       AnyString,              // *
+                       End,                    // end of pattern
+                       True                    // always succeeds
+               };
+       }
+}
diff --git a/mcs/class/System/System.IO/WindowsWatcher.cs b/mcs/class/System/System.IO/WindowsWatcher.cs
new file mode 100644 (file)
index 0000000..9a3f930
--- /dev/null
@@ -0,0 +1,31 @@
+// 
+// System.IO.WindowsWatcher.cs: windows IFileWatcher
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (c) 2004 Novell, Inc. (http://www.novell.com)
+//
+
+namespace System.IO {
+       class WindowsWatcher : IFileWatcher
+       {
+               private WindowsWatcher ()
+               {
+               }
+               
+               public static bool GetInstance (out IFileWatcher watcher)
+               {
+                       throw new NotSupportedException ();
+               }
+               
+               public void StartDispatching (FileSystemWatcher fsw)
+               {
+               }
+
+               public void StopDispatching (FileSystemWatcher fsw)
+               {
+               }
+       }
+}
+
index 544b32264f95bacec75ad60d6fb57eba5dde51a1..242cbf70e97a22ea9954eb959cea7dbbef500465 100755 (executable)
@@ -384,11 +384,15 @@ System.Diagnostics/TraceLevel.cs
 System.Diagnostics/TraceListenerCollection.cs
 System.Diagnostics/TraceListener.cs
 System.Diagnostics/TraceSwitch.cs
+System.IO/DefaultWatcher.cs
 System.IO/ErrorEventArgs.cs
 System.IO/ErrorEventHandler.cs
+System.IO/FAMWatcher.cs
+System.IO/FileAction.cs
 System.IO/FileSystemEventArgs.cs
 System.IO/FileSystemEventHandler.cs
 System.IO/FileSystemWatcher.cs
+System.IO/IFileWatcher.cs
 System.IO/InternalBufferOverflowException.cs
 System.IO/IODescriptionAttribute.cs
 System.IO/MonoIO.cs
@@ -396,8 +400,10 @@ System.IO/MonoIOError.cs
 System.IO/NotifyFilters.cs
 System.IO/RenamedEventArgs.cs
 System.IO/RenamedEventHandler.cs
+System.IO/SearchPattern.cs
 System.IO/WaitForChangedResult.cs
 System.IO/WatcherChangeTypes.cs
+System.IO/WindowsWatcher.cs
 System.Net/AuthenticationManager.cs
 System.Net/Authorization.cs
 System.Net/BasicClient.cs
index 4562ac9f67c10d4a01cd5644a57c3e6e4974b4c3..e9b68a5d41189075d4132afa1136a3ac64873cbb 100644 (file)
@@ -19,6 +19,7 @@ System.ComponentModel.Design/ServiceContainerTest.cs
 System.Diagnostics/TraceTest.cs
 System.Diagnostics/SwitchesTest.cs
 System.Diagnostics/DiagnosticsConfigurationHandlerTest.cs
+System.IO/FileSystemWatcherTest.cs
 System.Net/CookieCollectionTest.cs
 System.Net/CookieTest.cs
 System.Net/CredentialCacheTest.cs
diff --git a/mcs/class/System/Test/System.IO/ChangeLog b/mcs/class/System/Test/System.IO/ChangeLog
new file mode 100644 (file)
index 0000000..dc67d5f
--- /dev/null
@@ -0,0 +1,4 @@
+2004-01-16  Gonzalo Paniagua Javier <gonzalo@ximian.com>
+
+       * FileSystemWatcherTest.cs: new test.
+
diff --git a/mcs/class/System/Test/System.IO/FileSystemWatcherTest.cs b/mcs/class/System/Test/System.IO/FileSystemWatcherTest.cs
new file mode 100755 (executable)
index 0000000..b3e7348
--- /dev/null
@@ -0,0 +1,86 @@
+// FileSystemWatcherTest.cs - NUnit Test Cases for the System.IO.FileSystemWatcher class
+//
+// Authors:
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//
+// (C) 2004 Novell, Inc.  http://www.novell.com
+// 
+
+using NUnit.Framework;
+using System;
+using System.IO;
+
+namespace MonoTests.System.IO
+{
+       [TestFixture]
+       public class FileSystemWatcherTest : Assertion
+       {
+               [Test]
+               public void CheckDefaults ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher ();
+                       AssertEquals ("#01", fw.EnableRaisingEvents, false);
+                       AssertEquals ("#02", fw.Filter, "*.*");
+                       AssertEquals ("#03", fw.IncludeSubdirectories, false);
+                       AssertEquals ("#04", fw.InternalBufferSize, 8192);
+                       AssertEquals ("#05", fw.NotifyFilter, NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite);
+                       AssertEquals ("#06", fw.Path, "");
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentNullException))]
+               public void CheckCtor1 ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher (null);
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void CheckCtor2 ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher ("");
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentException))]
+               public void CheckCtor3 ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher ("notexistsblahblah");
+               }
+
+               [Test]
+               [ExpectedException (typeof (ArgumentNullException))]
+               public void CheckCtor4 ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher (Path.GetTempPath (), null);
+               }
+
+               [Test]
+               // Doesn't throw here :-?
+               // [ExpectedException (typeof (ArgumentException))]
+               public void CheckCtor5 ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher (Path.GetTempPath (), "invalidpath|");
+                       fw = new FileSystemWatcher (Path.GetTempPath (), "*");
+               }
+
+               [Test]
+               // ...But here it does...
+               [ExpectedException (typeof (ArgumentException))]
+               public void CheckInvalidPath ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher (Path.GetTempPath (), "invalidpath|");
+                       fw.Path = "invalidpath|";
+               }
+
+               [Test]
+               // ...and here too
+               [ExpectedException (typeof (ArgumentException))]
+               public void CheckPathWildcard ()
+               {
+                       FileSystemWatcher fw = new FileSystemWatcher (Path.GetTempPath (), "*");
+                       fw.Path = "*";
+               }
+       }
+}
+