/// <summary> /// Create observer for one file. /// </summary> /// <param name="filesystem"></param> /// <param name="patternInfo"></param> /// <param name="observer"></param> /// <param name="state"></param> /// <param name="eventDispatcher"></param> public PatternObserver(IFileSystem filesystem, GlobPatternInfo patternInfo, IObserver <IEvent> observer, object state, IEventDispatcher eventDispatcher = default) : base(filesystem, patternInfo.Pattern, observer, state, eventDispatcher) { this.changeToken = FileProvider.Watch(patternInfo.Pattern); this.previousSnapshot = ReadSnapshot(); this.watcher = changeToken.RegisterChangeCallback(OnEvent, this); }
/// <summary> /// Adds glob pattern. /// "**" Matched to for any string of characters including directory separator. /// "*" Matched for any string of characters within the same directory. /// "?" Matched for one character excluding directory separator. /// </summary> /// <param name="pattern"></param> /// <returns></returns> public FileScanner AddGlobPattern(string pattern) { if (pattern == null) { return(this); } GlobPatternInfo info = new GlobPatternInfo(pattern); // Add to pattern set PatternSet set; if (!patterns.TryGetValue(info.Stem, out set)) { patterns[info.Stem] = set = new PatternSet(); } set.AddGlobPattern(info, info.SuffixDepth); return(this); }
/// <summary> /// Attach an <paramref name="observer"/> on to a single file or directory. /// Observing a directory will observe the whole subtree. /// /// WARNING: The Observe implementation browses the subtree of the watched directory path in order to create delta of changes. /// </summary> /// <param name="filter">path to file or directory. The directory separator is "/". The root is without preceding slash "", e.g. "dir/dir2"</param> /// <param name="observer"></param> /// <param name="state">(optional) </param> /// <param name="eventDispatcher">(optional) event dispatcher</param> /// <param name="option">(optional) operation specific option; capability constraint, a session, security token or credential. Used for authenticating, authorizing or restricting the operation.</param> /// <returns>dispose handle</returns> /// <exception cref="IOException">On unexpected IO error</exception> /// <exception cref="SecurityException">If caller did not have permission</exception> /// <exception cref="ArgumentNullException"><paramref name="filter"/> is null</exception> /// <exception cref="ArgumentException"><paramref name="filter"/> contains only white space, or contains one or more invalid characters</exception> /// <exception cref="NotSupportedException">The <see cref="IFileSystem"/> doesn't support observe</exception> /// <exception cref="UnauthorizedAccessException">The access requested is not permitted by the operating system for the specified path.</exception> /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.</exception> /// <exception cref="InvalidOperationException">If <paramref name="filter"/> refers to a non-file device, such as "con:", "com1:", "lpt1:", etc.</exception> /// <exception cref="ObjectDisposedException"/> public virtual IFileSystemObserver Observe(string filter, IObserver <IEvent> observer, object state = null, IEventDispatcher eventDispatcher = default, IOption option = null) { // Assert observe is enabled. if (!CanObserve || !option.CanObserve(true)) { throw new NotSupportedException(nameof(Observe)); } // Assert not diposed IFileProvider fp = fileProvider; if (fp == null) { return(new DummyObserver(this, filter, observer, state, eventDispatcher)); } // Parse filter GlobPatternInfo patternInfo = new GlobPatternInfo(filter); // Monitor single file (or dir, we don't know "dir") if (patternInfo.SuffixDepth == 0) { // Create observer that watches one file FileObserver handle = new FileObserver(this, filter, observer, state, eventDispatcher, option.OptionIntersection(this.options)); // Send handle observer.OnNext(new StartEvent(handle, DateTimeOffset.UtcNow)); // Return handle return(handle); } else // Has wildcards, e.g. "**/file.txt" { // Create handle PatternObserver handle = new PatternObserver(this, patternInfo, observer, state, eventDispatcher); // Send handle observer.OnNext(new StartEvent(handle, DateTimeOffset.UtcNow)); // Return handle return(handle); } }
public void GlobPatternInfo() { { GlobPatternInfo gpi = new GlobPatternInfo(""); Assert.AreEqual("", gpi.Stem); Assert.AreEqual("", gpi.Suffix); Assert.AreEqual(0, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir/file.txt"); Assert.AreEqual("dir/dir/file.txt", gpi.Stem); Assert.AreEqual("", gpi.Suffix); Assert.AreEqual(0, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("*.txt"); Assert.AreEqual("", gpi.Stem); Assert.AreEqual("*.txt", gpi.Suffix); Assert.AreEqual(1, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("**.txt"); Assert.AreEqual("", gpi.Stem); Assert.AreEqual("**.txt", gpi.Suffix); Assert.AreEqual(int.MaxValue, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("/*.txt"); Assert.AreEqual("/", gpi.Stem); Assert.AreEqual("*.txt", gpi.Suffix); Assert.AreEqual(1, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("*/*.txt"); Assert.AreEqual("", gpi.Stem); Assert.AreEqual("*/*.txt", gpi.Suffix); Assert.AreEqual(2, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("/**.txt"); Assert.AreEqual("/", gpi.Stem); Assert.AreEqual("**.txt", gpi.Suffix); Assert.AreEqual(int.MaxValue, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir/*/*.txt"); Assert.AreEqual("dir/dir/", gpi.Stem); Assert.AreEqual("*/*.txt", gpi.Suffix); Assert.AreEqual(2, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir?/*/*.txt"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("dir?/*/*.txt", gpi.Suffix); Assert.AreEqual(3, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir/dir/*/*.txt"); Assert.AreEqual("dir/dir/dir/", gpi.Stem); Assert.AreEqual("*/*.txt", gpi.Suffix); Assert.AreEqual(2, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir/dir?/*/*.txt"); Assert.AreEqual("dir/dir/", gpi.Stem); Assert.AreEqual("dir?/*/*.txt", gpi.Suffix); Assert.AreEqual(3, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/dir/dir?/*/**.txt"); Assert.AreEqual("dir/dir/", gpi.Stem); Assert.AreEqual("dir?/*/**.txt", gpi.Suffix); Assert.AreEqual(int.MaxValue, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/*/dir/dir/dir/file.txt"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("*/dir/dir/dir/file.txt", gpi.Suffix); Assert.AreEqual(5, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/**"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("**", gpi.Suffix); Assert.AreEqual(int.MaxValue, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/*"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("*", gpi.Suffix); Assert.AreEqual(1, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("", gpi.Suffix); Assert.AreEqual(0, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir"); Assert.AreEqual("dir", gpi.Stem); Assert.AreEqual("", gpi.Suffix); Assert.AreEqual(0, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/file.txt"); Assert.AreEqual("dir/file.txt", gpi.Stem); Assert.AreEqual("", gpi.Suffix); Assert.AreEqual(0, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/file*.txt"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("file*.txt", gpi.Suffix); Assert.AreEqual(1, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir/file**.txt"); Assert.AreEqual("dir/", gpi.Stem); Assert.AreEqual("file**.txt", gpi.Suffix); Assert.AreEqual(int.MaxValue, gpi.SuffixDepth); } { GlobPatternInfo gpi = new GlobPatternInfo("dir*/file.txt"); Assert.AreEqual("", gpi.Stem); Assert.AreEqual("dir*/file.txt", gpi.Suffix); Assert.AreEqual(2, gpi.SuffixDepth); } }