October 16, 2023

Bit Operations

An extension class that allows various bitwise operations on an integer to be performed when dealing with a [Flags] Enum type.

// Bit wise operations on an integer as an extensions class
public static class IntBitwiseOperationsExtender
{
    // Returns bits set in lhs or rhs or both
    public static int BitwiseUnion(this int lhs, int rhs)
    {
        int bitWiseOr = lhs | rhs;
        return bitWiseOr;
    }

    // Return bits set common to both lhs and rhs
    public static int BitwiseIntersection(this int lhs, int rhs)
    {
        return lhs & rhs;
    }

    // Returns bits set in lhs or rhs but not in both
    public static int BitwiseExclusiveOr(this int lhs, int rhs)
    {
        int exclusiveOr = BitwiseUnion(lhs, rhs) - BitwiseIntersection(lhs, rhs);
        return exclusiveOr;
    }

    // Return lhs bits inverted, 0s becomes 1s and vice versa
    public static int BitwiseInvert(this int lhs)
    {
        int bitWiseOr = ~lhs;
        return bitWiseOr;
    }

    // Return lhs bits set minus any that are also set in the rhs
    public static int BitwiseRemove(this int lhs, int rhs)
    {
        int common = BitwiseIntersection(lhs, rhs); // Find the bits common to both sides
        int res = (int)lhs - (int)common;
        return res;
    }

    // Return a value that has all the bits set either in the lhs part or in the rhs part or both
    public static int BitwiseOr(this int lhs, int rhs)
    {
        return BitwiseUnion(lhs, rhs); // Same as a Bitwise Union
    }

    // Return true if lhs contains all the bits set within rhs
    public static bool BitwiseContains(this int lhs, int rhs)
    {
        int common = lhs & rhs;
        return (common == rhs);
    }

    // Return true if lhs contains one of the bits set within rhs
    public static bool BitwiseContainsOneOf(this int lhs, int rhs)
    {
        int common = lhs & rhs;
        return common > 0;
    }
}

October 14, 2023

Simple Line Editor 2

Here is the new LineEditor, it can work on a file or a string. This timing using the power of LINQ.

// Here is the definition of a line edit
public interface ILineEdit
{
    string ApplyEdit(string input);
}

/// <summary>
/// Perform a bunch of edits on a text file or string on a line by line basis. 
/// Only search and replace type edits are available.
/// </summary>
/// <summary>
/// Perform a bunch of edits on a IEnumerable<string>. 
/// </summary>
public class TextLineEditor
{
    private List<ILineEdit> _pendingEdits = new List<ILineEdit>();

    public TextLineEditor()
    {
    }

    public IEnumerable<string> ApplyEdits(IEnumerable<string> inputs)
    {
        foreach (var line in inputs)
        {
            string tmp = line;
            foreach (ILineEdit edit in _pendingEdits)
            {
                tmp = edit.ApplyEdit(tmp);
            }
            yield return tmp;
        }
    }

    public void AddEdit(ILineEdit edit)
    {
        Trace.Assert(edit != null);
        _pendingEdits.Add(edit);
    }
}

We can use this as the basis of a line by line text file ediitor:

/// <summary>
/// Perform a bunch of edits on a text file 
/// Only search and replace type edits are available.
/// </summary>
public class FileTextLineEditor : TextLineEditor
{
    public void ApplyEditsToFile(string inputPath, bool keepOriginal = true)
    {
        Debug.Assert(!string.IsNullOrWhiteSpace(inputPath));
        var inputFileInfo = new FileInfo(inputPath);
        Debug.Assert(inputFileInfo.Exists);

        var outputPath = inputPath + ".new";
        var outputFileInfo = new FileInfo(outputPath);
        Debug.Assert(!outputFileInfo.Exists);

        File.WriteAllLines(outputFileInfo.FullName, ApplyEdits(File.ReadLines(inputFileInfo.FullName)));
        
        inputFileInfo.MoveTo(inputPath + ".bak");
        outputFileInfo.Refresh();
        outputFileInfo.MoveTo(inputPath);
        if (!keepOriginal)
        {
            File.Delete(inputPath + ".bak");
        }
    }
}
Here is a LineEdit example. Note that it can do deletes by replacing with a blank string.

public class SearchAndReplace : ILineEdit
{
    public enum RegExUsage
    {
        None = 0,
        Use
    }

    public SearchAndReplace()
    {
    }

    public SearchAndReplace(string searchFor, string replaceWith, bool extendedChars = false, RegExUsage regExUse = RegExUsage.None)
    {
        if (string.IsNullOrEmpty(searchFor))
            throw new ArgumentException("Cannot be null or an empty string", "searchFor");
        if (replaceWith == null)
            throw new ArgumentException("Cannot be null", "replaceWith");
        this.SearchFor = searchFor;
        this.ReplaceWith = replaceWith;
        this.ExtendedChars = extendedChars;
    }

    public bool IsRegEx { get; set; } = false;
    public string SearchFor { get; set; } = "";
    public string ReplaceWith { get; set; } = "";
    public bool ExtendedChars { get; set; } = false;

    public string ApplyEdit(string input)
    {
        if (ExtendedChars)
        {
            SearchFor = SearchFor.Replace("\\t","\t").Replace("\\n","\n").Replace("\\r","\r");
        }
        return (IsRegEx) ? SearchReplaceInString(input) : SearchReplaceInStringRegEx(input);
    }

    private string SearchReplaceInString(string input)
    {
        string output = input.Replace(SearchFor, ReplaceWith);
        return output;
    }

    private string SearchReplaceInStringRegEx(string input)
    {
        string output = Regex.Replace(input, SearchFor, ReplaceWith);
        return output;
    }
}

Here is an example usage that attempts to replace usage of Moq in a CS file to usage of NSubstitute:
void Main()
{
    string inputFile = @"a:/path/SomeFileName.cs";

    FileTextLineEditor resEditor = new();
    resEditor.AddEdits(this.Edits());
    resEditor.ApplyEditsToFile(inputFile, keepOriginal: false);
}

internal IEnumerable<ILineEdit> Edits()
{
    yield return new SearchAndReplace("using Moq", "using NSubstitute");
    yield return new SearchAndReplace("MockBehavior.Strict", "");
    yield return new SearchAndReplace("MockBehavior.Loose", "");
    yield return new SearchAndReplace(".Object", "");
    yield return new SearchAndReplace("It.IsAny", "Arg.Any");
    yield return new SearchAndReplace("It.Is", "Arg.Is");
    yield return new SearchAndReplace(@"(new\sMock\<([A-Za-z\<\>\-_]+)\>)", @"Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Mock\<([A-Za-z\<\>\-_]+)\>\s([A-Za-z\<\>\-_]+)\s\=\snew)", @"var $3 = Substitute.For<$2>", false, SearchAndReplace.RegExUsage.Use);
    // yield return new SearchAndReplace(@" = new()", @" = Substitute.For(??)");
    //yield return new SearchAndReplace(@"(^\sMock\<([A-Za-z\<\>\-_]+)", "var", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Setup\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(SetupGet\([a-z]+\s\=\>\s[a-z]+\.)", "", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace(@"(Verify\([a-z]+\s\=\>\s[a-z]+)", "Received(?)", false, SearchAndReplace.RegExUsage.Use);
    yield return new SearchAndReplace("Times.Once", "1");
    yield return new SearchAndReplace("Times.Never", "0");
    yield return new SearchAndReplace(@"Returns\(\(\)\s\=\>\s", "Returns(");
}