mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-18 07:00:04 +02:00
Make some classes internal + chnage some files to file-scoped namespaces
This commit is contained in:
@@ -1,35 +1,34 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.Exceptions
|
namespace WireMock.Exceptions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WireMockException
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Exception" />
|
||||||
|
public class WireMockException : Exception
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WireMockException
|
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="Exception" />
|
public WireMockException()
|
||||||
public class WireMockException : Exception
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
}
|
||||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public WireMockException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The message that describes the error.</param>
|
/// <param name="message">The message that describes the error.</param>
|
||||||
public WireMockException(string message) : base(message)
|
public WireMockException(string message) : base(message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The message.</param>
|
/// <param name="message">The message.</param>
|
||||||
/// <param name="inner">The inner.</param>
|
/// <param name="inner">The inner.</param>
|
||||||
public WireMockException(string message, Exception inner) : base(message, inner)
|
public WireMockException(string message, Exception inner) : base(message, inner)
|
||||||
{
|
{
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,179 +3,178 @@ using System.IO;
|
|||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Handlers
|
namespace WireMock.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
||||||
|
/// </summary>
|
||||||
|
public class LocalFileSystemHandler : IFileSystemHandler
|
||||||
{
|
{
|
||||||
|
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
|
||||||
|
private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched");
|
||||||
|
|
||||||
|
private readonly string _rootFolder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LocalFileSystemHandler : IFileSystemHandler
|
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
|
||||||
{
|
{
|
||||||
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
|
}
|
||||||
private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched");
|
|
||||||
|
|
||||||
private readonly string _rootFolder;
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rootFolder">The root folder.</param>
|
||||||
|
public LocalFileSystemHandler(string rootFolder)
|
||||||
|
{
|
||||||
|
_rootFolder = rootFolder;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
||||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
public virtual bool FolderExists(string path)
|
||||||
/// </summary>
|
{
|
||||||
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
|
return Directory.Exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
||||||
|
public virtual void CreateFolder(string path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
||||||
|
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
|
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
|
||||||
|
public virtual string GetMappingFolder()
|
||||||
|
{
|
||||||
|
return Path.Combine(_rootFolder, AdminMappingsFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
||||||
|
public virtual string ReadMappingFile(string path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
|
return File.ReadAllText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
|
||||||
|
public virtual void WriteMappingFile(string path, string text)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path, nameof(path));
|
||||||
|
Guard.NotNull(text, nameof(text));
|
||||||
|
|
||||||
|
File.WriteAllText(path, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
||||||
|
public virtual byte[] ReadResponseBodyAsFile(string path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
path = PathUtils.CleanPath(path);
|
||||||
|
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||||
|
// Else the path will just be as-is.
|
||||||
|
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||||
|
public virtual string ReadResponseBodyAsString(string path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
path = PathUtils.CleanPath(path);
|
||||||
|
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||||
|
// Else the path will just be as-is.
|
||||||
|
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||||
|
public virtual bool FileExists(string filename)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
|
return File.Exists(AdjustPathForMappingFolder(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual void WriteFile(string filename, byte[] bytes)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
Guard.NotNull(bytes);
|
||||||
|
|
||||||
|
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual void WriteFile(string folder, string filename, byte[] bytes)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(folder);
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
Guard.NotNull(bytes);
|
||||||
|
|
||||||
|
File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
||||||
|
public virtual void DeleteFile(string filename)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
|
File.Delete(AdjustPathForMappingFolder(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
||||||
|
public virtual byte[] ReadFile(string filename)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
|
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
|
||||||
|
public virtual string ReadFileAsString(string filename)
|
||||||
|
{
|
||||||
|
return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.GetUnmatchedRequestsFolder"/>
|
||||||
|
public virtual string GetUnmatchedRequestsFolder()
|
||||||
|
{
|
||||||
|
return Path.Combine(_rootFolder, UnmatchedRequestsFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
||||||
|
public virtual void WriteUnmatchedRequest(string filename, string text)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
Guard.NotNull(text);
|
||||||
|
|
||||||
|
var folder = GetUnmatchedRequestsFolder();
|
||||||
|
if (!FolderExists(folder))
|
||||||
{
|
{
|
||||||
|
CreateFolder(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
File.WriteAllText(Path.Combine(folder, filename), text);
|
||||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="rootFolder">The root folder.</param>
|
|
||||||
public LocalFileSystemHandler(string rootFolder)
|
|
||||||
{
|
|
||||||
_rootFolder = rootFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
/// <summary>
|
||||||
public virtual bool FolderExists(string path)
|
/// Adjusts the path to the MappingFolder.
|
||||||
{
|
/// </summary>
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
/// <param name="filename">The path.</param>
|
||||||
|
/// <returns>Adjusted path</returns>
|
||||||
return Directory.Exists(path);
|
private string AdjustPathForMappingFolder(string filename)
|
||||||
}
|
{
|
||||||
|
return Path.Combine(GetMappingFolder(), filename);
|
||||||
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
|
||||||
public virtual void CreateFolder(string path)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
|
||||||
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
|
|
||||||
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
|
|
||||||
public virtual string GetMappingFolder()
|
|
||||||
{
|
|
||||||
return Path.Combine(_rootFolder, AdminMappingsFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
|
||||||
public virtual string ReadMappingFile(string path)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
|
|
||||||
return File.ReadAllText(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
|
|
||||||
public virtual void WriteMappingFile(string path, string text)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
Guard.NotNull(text, nameof(text));
|
|
||||||
|
|
||||||
File.WriteAllText(path, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
|
||||||
public virtual byte[] ReadResponseBodyAsFile(string path)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
path = PathUtils.CleanPath(path);
|
|
||||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
|
||||||
// Else the path will just be as-is.
|
|
||||||
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
|
||||||
public virtual string ReadResponseBodyAsString(string path)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
path = PathUtils.CleanPath(path);
|
|
||||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
|
||||||
// Else the path will just be as-is.
|
|
||||||
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
|
||||||
public virtual bool FileExists(string filename)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
|
||||||
|
|
||||||
return File.Exists(AdjustPathForMappingFolder(filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void WriteFile(string filename, byte[] bytes)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
|
||||||
Guard.NotNull(bytes, nameof(bytes));
|
|
||||||
|
|
||||||
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void WriteFile(string folder, string filename, byte[] bytes)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(folder);
|
|
||||||
Guard.NotNullOrEmpty(filename);
|
|
||||||
Guard.NotNull(bytes);
|
|
||||||
|
|
||||||
File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
|
||||||
public virtual void DeleteFile(string filename)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
|
||||||
|
|
||||||
File.Delete(AdjustPathForMappingFolder(filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
|
||||||
public virtual byte[] ReadFile(string filename)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
|
||||||
|
|
||||||
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
|
|
||||||
public virtual string ReadFileAsString(string filename)
|
|
||||||
{
|
|
||||||
return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename))));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.GetUnmatchedRequestsFolder"/>
|
|
||||||
public virtual string GetUnmatchedRequestsFolder()
|
|
||||||
{
|
|
||||||
return Path.Combine(_rootFolder, UnmatchedRequestsFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
|
||||||
public virtual void WriteUnmatchedRequest(string filename, string text)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
|
||||||
Guard.NotNull(text, nameof(text));
|
|
||||||
|
|
||||||
var folder = GetUnmatchedRequestsFolder();
|
|
||||||
if (!FolderExists(folder))
|
|
||||||
{
|
|
||||||
CreateFolder(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(Path.Combine(folder, filename), text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adjusts the path to the MappingFolder.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The path.</param>
|
|
||||||
/// <returns>Adjusted path</returns>
|
|
||||||
private string AdjustPathForMappingFolder(string filename)
|
|
||||||
{
|
|
||||||
return Path.Combine(GetMappingFolder(), filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Matchers;
|
namespace WireMock.Matchers;
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.Models
|
namespace WireMock.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeSettingsModel: Start, End and TTL
|
||||||
|
/// </summary>
|
||||||
|
public class TimeSettings : ITimeSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// TimeSettingsModel: Start, End and TTL
|
public DateTime? Start { get; set; }
|
||||||
/// </summary>
|
|
||||||
public class TimeSettings : ITimeSettings
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public DateTime? Start { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public DateTime? End { get; set; }
|
public DateTime? End { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int? TTL { get; set; }
|
public int? TTL { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace WireMock.Util;
|
|||||||
/// http://www.unicode.org/versions/corrigendum1.html
|
/// http://www.unicode.org/versions/corrigendum1.html
|
||||||
/// http://www.ietf.org/rfc/rfc2279.txt
|
/// http://www.ietf.org/rfc/rfc2279.txt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BytesEncodingUtils
|
internal static class BytesEncodingUtils
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries the get the Encoding from an array of bytes.
|
/// Tries the get the Encoding from an array of bytes.
|
||||||
|
|||||||
@@ -1,86 +1,85 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||||
|
/// <inheritdoc cref="ObservableCollection{T}" />
|
||||||
|
internal class ConcurrentObservableCollection<T> : ObservableCollection<T>
|
||||||
{
|
{
|
||||||
|
private readonly object _lockObject = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurrentObservableCollection`1" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public ConcurrentObservableCollection() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe.
|
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
/// <param name="list">The list from which the elements are copied.</param>
|
||||||
/// <inheritdoc cref="ObservableCollection{T}" />
|
public ConcurrentObservableCollection(List<T> list) : base(list) { }
|
||||||
internal class ConcurrentObservableCollection<T> : ObservableCollection<T>
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection">The collection from which the elements are copied.</param>
|
||||||
|
public ConcurrentObservableCollection(IEnumerable<T> collection) : base(collection) { }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ObservableCollection{T}.ClearItems"/>
|
||||||
|
protected override void ClearItems()
|
||||||
{
|
{
|
||||||
private readonly object _lockObject = new object();
|
lock (_lockObject)
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurrentObservableCollection`1" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public ConcurrentObservableCollection() { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified list.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="list">The list from which the elements are copied.</param>
|
|
||||||
public ConcurrentObservableCollection(List<T> list) : base(list) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified collection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="collection">The collection from which the elements are copied.</param>
|
|
||||||
public ConcurrentObservableCollection(IEnumerable<T> collection) : base(collection) { }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ObservableCollection{T}.ClearItems"/>
|
|
||||||
protected override void ClearItems()
|
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
base.ClearItems();
|
||||||
{
|
|
||||||
base.ClearItems();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ObservableCollection{T}.RemoveItem"/>
|
/// <inheritdoc cref="ObservableCollection{T}.RemoveItem"/>
|
||||||
protected override void RemoveItem(int index)
|
protected override void RemoveItem(int index)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
base.RemoveItem(index);
|
||||||
{
|
|
||||||
base.RemoveItem(index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ObservableCollection{T}.InsertItem"/>
|
/// <inheritdoc cref="ObservableCollection{T}.InsertItem"/>
|
||||||
protected override void InsertItem(int index, T item)
|
protected override void InsertItem(int index, T item)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
base.InsertItem(index, item);
|
||||||
{
|
|
||||||
base.InsertItem(index, item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ObservableCollection{T}.SetItem"/>
|
/// <inheritdoc cref="ObservableCollection{T}.SetItem"/>
|
||||||
protected override void SetItem(int index, T item)
|
protected override void SetItem(int index, T item)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
base.SetItem(index, item);
|
||||||
{
|
|
||||||
base.SetItem(index, item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ObservableCollection{T}.MoveItem"/>
|
/// <inheritdoc cref="ObservableCollection{T}.MoveItem"/>
|
||||||
protected override void MoveItem(int oldIndex, int newIndex)
|
protected override void MoveItem(int oldIndex, int newIndex)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
base.MoveItem(oldIndex, newIndex);
|
||||||
{
|
|
||||||
base.MoveItem(oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<T> ToList()
|
public List<T> ToList()
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
{
|
{
|
||||||
lock (_lockObject)
|
return Items.ToList();
|
||||||
{
|
|
||||||
return Items.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,251 +4,250 @@ using System.IO;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FileSystemWatcher" />
|
||||||
|
public class EnhancedFileSystemWatcher : FileSystemWatcher
|
||||||
{
|
{
|
||||||
|
#region Private Members
|
||||||
|
// Default Watch Interval in Milliseconds
|
||||||
|
private const int DefaultWatchInterval = 100;
|
||||||
|
|
||||||
|
// This Dictionary keeps the track of when an event occurred last for a particular file
|
||||||
|
private ConcurrentDictionary<string, DateTime> _lastFileEvent = new();
|
||||||
|
|
||||||
|
// Watch Interval in Milliseconds
|
||||||
|
private int _interval;
|
||||||
|
|
||||||
|
// Timespan created when interval is set
|
||||||
|
private TimeSpan _recentTimeSpan;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Properties
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file.
|
/// Interval, in milliseconds, within which events are considered "recent".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FileSystemWatcher" />
|
[PublicAPI]
|
||||||
public class EnhancedFileSystemWatcher : FileSystemWatcher
|
public int Interval
|
||||||
{
|
{
|
||||||
#region Private Members
|
get => _interval;
|
||||||
// Default Watch Interval in Milliseconds
|
set
|
||||||
private const int DefaultWatchInterval = 100;
|
|
||||||
|
|
||||||
// This Dictionary keeps the track of when an event occurred last for a particular file
|
|
||||||
private ConcurrentDictionary<string, DateTime> _lastFileEvent = new();
|
|
||||||
|
|
||||||
// Watch Interval in Milliseconds
|
|
||||||
private int _interval;
|
|
||||||
|
|
||||||
// Timespan created when interval is set
|
|
||||||
private TimeSpan _recentTimeSpan;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Properties
|
|
||||||
/// <summary>
|
|
||||||
/// Interval, in milliseconds, within which events are considered "recent".
|
|
||||||
/// </summary>
|
|
||||||
[PublicAPI]
|
|
||||||
public int Interval
|
|
||||||
{
|
{
|
||||||
get => _interval;
|
_interval = value;
|
||||||
set
|
|
||||||
{
|
|
||||||
_interval = value;
|
|
||||||
|
|
||||||
// Set timespan based on the value passed
|
// Set timespan based on the value passed
|
||||||
_recentTimeSpan = new TimeSpan(0, 0, 0, 0, value);
|
_recentTimeSpan = new TimeSpan(0, 0, 0, 0, value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows user to set whether to filter recent events.
|
|
||||||
/// If this is set a false, this class behaves like System.IO.FileSystemWatcher class.
|
|
||||||
/// </summary>
|
|
||||||
[PublicAPI]
|
|
||||||
public bool FilterRecentEvents { get; set; }
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="interval">The interval.</param>
|
|
||||||
public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval)
|
|
||||||
{
|
|
||||||
Guard.Condition(interval, i => i >= 0);
|
|
||||||
|
|
||||||
InitializeMembers(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
|
||||||
/// <param name="interval">The interval.</param>
|
|
||||||
public EnhancedFileSystemWatcher(string path, int interval = DefaultWatchInterval) : base(path)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path);
|
|
||||||
Guard.Condition(interval, i => i >= 0);
|
|
||||||
|
|
||||||
InitializeMembers(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
|
||||||
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
|
|
||||||
/// <param name="interval">The interval.</param>
|
|
||||||
public EnhancedFileSystemWatcher(string path, string filter, int interval = DefaultWatchInterval) : base(path, filter)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(path);
|
|
||||||
Guard.NotNullOrEmpty(filter);
|
|
||||||
Guard.Condition(interval, i => i >= 0);
|
|
||||||
|
|
||||||
InitializeMembers(interval);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
// These events hide the events from the base class.
|
|
||||||
// We want to raise these events appropriately and we do not want the
|
|
||||||
// users of this class subscribing to these events of the base class accidentally
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is changed.
|
|
||||||
/// </summary>
|
|
||||||
public new event FileSystemEventHandler? Changed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is created.
|
|
||||||
/// </summary>
|
|
||||||
public new event FileSystemEventHandler? Created;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is deleted.
|
|
||||||
/// </summary>
|
|
||||||
public new event FileSystemEventHandler? Deleted;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is renamed.
|
|
||||||
/// </summary>
|
|
||||||
public new event RenamedEventHandler? Renamed;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Protected Methods to raise the Events for this class
|
|
||||||
/// <summary>
|
|
||||||
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Changed" /> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
|
||||||
protected new virtual void OnChanged(FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
Changed?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Created" /> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
|
||||||
protected new virtual void OnCreated(FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
Created?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Deleted" /> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
|
||||||
protected new virtual void OnDeleted(FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
Deleted?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Renamed" /> event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="e">A <see cref="T:System.IO.RenamedEventArgs" /> that contains the event data.</param>
|
|
||||||
protected new virtual void OnRenamed(RenamedEventArgs e)
|
|
||||||
{
|
|
||||||
Renamed?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
/// <summary>
|
|
||||||
/// This Method Initializes the private members.
|
|
||||||
/// Interval is set to its default value of 100 millisecond.
|
|
||||||
/// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized.
|
|
||||||
/// We subscribe to the base class events.
|
|
||||||
/// </summary>
|
|
||||||
private void InitializeMembers(int interval = 100)
|
|
||||||
{
|
|
||||||
Interval = interval;
|
|
||||||
FilterRecentEvents = true;
|
|
||||||
_lastFileEvent = new ConcurrentDictionary<string, DateTime>();
|
|
||||||
|
|
||||||
base.Created += OnCreated;
|
|
||||||
base.Changed += OnChanged;
|
|
||||||
base.Deleted += OnDeleted;
|
|
||||||
base.Renamed += OnRenamed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method searches the dictionary to find out when the last event occurred
|
|
||||||
/// for a particular file. If that event occurred within the specified timespan
|
|
||||||
/// it returns true, else false
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The filename to be checked</param>
|
|
||||||
/// <returns>True if an event has occurred within the specified interval, False otherwise</returns>
|
|
||||||
private bool HasAnotherFileEventOccurredRecently(string fileName)
|
|
||||||
{
|
|
||||||
// Check dictionary only if user wants to filter recent events otherwise return value stays false.
|
|
||||||
if (!FilterRecentEvents)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool retVal = false;
|
|
||||||
if (_lastFileEvent.ContainsKey(fileName))
|
|
||||||
{
|
|
||||||
// If dictionary contains the filename, check how much time has elapsed
|
|
||||||
// since the last event occurred. If the timespan is less that the
|
|
||||||
// specified interval, set return value to true
|
|
||||||
// and store current datetime in dictionary for this file
|
|
||||||
DateTime lastEventTime = _lastFileEvent[fileName];
|
|
||||||
DateTime currentTime = DateTime.Now;
|
|
||||||
TimeSpan timeSinceLastEvent = currentTime - lastEventTime;
|
|
||||||
retVal = timeSinceLastEvent < _recentTimeSpan;
|
|
||||||
_lastFileEvent[fileName] = currentTime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If dictionary does not contain the filename,
|
|
||||||
// no event has occurred in past for this file, so set return value to false
|
|
||||||
// and append filename along with current datetime to the dictionary
|
|
||||||
_lastFileEvent.TryAdd(fileName, DateTime.Now);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region FileSystemWatcher EventHandlers
|
|
||||||
// Base class Event Handlers. Check if an event has occurred recently and call method
|
|
||||||
// to raise appropriate event only if no recent event is detected
|
|
||||||
private void OnChanged(object sender, FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
|
||||||
{
|
|
||||||
OnChanged(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCreated(object sender, FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
|
||||||
{
|
|
||||||
OnCreated(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDeleted(object sender, FileSystemEventArgs e)
|
|
||||||
{
|
|
||||||
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
|
||||||
{
|
|
||||||
OnDeleted(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRenamed(object sender, RenamedEventArgs e)
|
|
||||||
{
|
|
||||||
if (!HasAnotherFileEventOccurredRecently(e.OldFullPath))
|
|
||||||
{
|
|
||||||
OnRenamed(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows user to set whether to filter recent events.
|
||||||
|
/// If this is set a false, this class behaves like System.IO.FileSystemWatcher class.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public bool FilterRecentEvents { get; set; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interval">The interval.</param>
|
||||||
|
public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval)
|
||||||
|
{
|
||||||
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
|
InitializeMembers(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
||||||
|
/// <param name="interval">The interval.</param>
|
||||||
|
public EnhancedFileSystemWatcher(string path, int interval = DefaultWatchInterval) : base(path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
|
InitializeMembers(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
||||||
|
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
|
||||||
|
/// <param name="interval">The interval.</param>
|
||||||
|
public EnhancedFileSystemWatcher(string path, string filter, int interval = DefaultWatchInterval) : base(path, filter)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
Guard.NotNullOrEmpty(filter);
|
||||||
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
|
InitializeMembers(interval);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
// These events hide the events from the base class.
|
||||||
|
// We want to raise these events appropriately and we do not want the
|
||||||
|
// users of this class subscribing to these events of the base class accidentally
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is changed.
|
||||||
|
/// </summary>
|
||||||
|
public new event FileSystemEventHandler? Changed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is created.
|
||||||
|
/// </summary>
|
||||||
|
public new event FileSystemEventHandler? Created;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is deleted.
|
||||||
|
/// </summary>
|
||||||
|
public new event FileSystemEventHandler? Deleted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is renamed.
|
||||||
|
/// </summary>
|
||||||
|
public new event RenamedEventHandler? Renamed;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Protected Methods to raise the Events for this class
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Changed" /> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
||||||
|
protected new virtual void OnChanged(FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
Changed?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Created" /> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
||||||
|
protected new virtual void OnCreated(FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
Created?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Deleted" /> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
|
||||||
|
protected new virtual void OnDeleted(FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
Deleted?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Renamed" /> event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">A <see cref="T:System.IO.RenamedEventArgs" /> that contains the event data.</param>
|
||||||
|
protected new virtual void OnRenamed(RenamedEventArgs e)
|
||||||
|
{
|
||||||
|
Renamed?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Methods
|
||||||
|
/// <summary>
|
||||||
|
/// This Method Initializes the private members.
|
||||||
|
/// Interval is set to its default value of 100 millisecond.
|
||||||
|
/// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized.
|
||||||
|
/// We subscribe to the base class events.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeMembers(int interval = 100)
|
||||||
|
{
|
||||||
|
Interval = interval;
|
||||||
|
FilterRecentEvents = true;
|
||||||
|
_lastFileEvent = new ConcurrentDictionary<string, DateTime>();
|
||||||
|
|
||||||
|
base.Created += OnCreated;
|
||||||
|
base.Changed += OnChanged;
|
||||||
|
base.Deleted += OnDeleted;
|
||||||
|
base.Renamed += OnRenamed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method searches the dictionary to find out when the last event occurred
|
||||||
|
/// for a particular file. If that event occurred within the specified timespan
|
||||||
|
/// it returns true, else false
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The filename to be checked</param>
|
||||||
|
/// <returns>True if an event has occurred within the specified interval, False otherwise</returns>
|
||||||
|
private bool HasAnotherFileEventOccurredRecently(string fileName)
|
||||||
|
{
|
||||||
|
// Check dictionary only if user wants to filter recent events otherwise return value stays false.
|
||||||
|
if (!FilterRecentEvents)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retVal = false;
|
||||||
|
if (_lastFileEvent.ContainsKey(fileName))
|
||||||
|
{
|
||||||
|
// If dictionary contains the filename, check how much time has elapsed
|
||||||
|
// since the last event occurred. If the timespan is less that the
|
||||||
|
// specified interval, set return value to true
|
||||||
|
// and store current datetime in dictionary for this file
|
||||||
|
DateTime lastEventTime = _lastFileEvent[fileName];
|
||||||
|
DateTime currentTime = DateTime.Now;
|
||||||
|
TimeSpan timeSinceLastEvent = currentTime - lastEventTime;
|
||||||
|
retVal = timeSinceLastEvent < _recentTimeSpan;
|
||||||
|
_lastFileEvent[fileName] = currentTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If dictionary does not contain the filename,
|
||||||
|
// no event has occurred in past for this file, so set return value to false
|
||||||
|
// and append filename along with current datetime to the dictionary
|
||||||
|
_lastFileEvent.TryAdd(fileName, DateTime.Now);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region FileSystemWatcher EventHandlers
|
||||||
|
// Base class Event Handlers. Check if an event has occurred recently and call method
|
||||||
|
// to raise appropriate event only if no recent event is detected
|
||||||
|
private void OnChanged(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
||||||
|
{
|
||||||
|
OnChanged(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCreated(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
||||||
|
{
|
||||||
|
OnCreated(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeleted(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
if (!HasAnotherFileEventOccurredRecently(e.FullPath))
|
||||||
|
{
|
||||||
|
OnDeleted(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRenamed(object sender, RenamedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!HasAnotherFileEventOccurredRecently(e.OldFullPath))
|
||||||
|
{
|
||||||
|
OnRenamed(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace WireMock.Util;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Port Utility class
|
/// Port Utility class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class PortUtils
|
internal static class PortUtils
|
||||||
{
|
{
|
||||||
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user