An example of madness. Some c# code I wrote a few years ago when I was ridiculously high.

I’m not posting this to my programming blog because it’s an example of really really bad code.

It seems that one fine morning while tweaking on having my source code all be perfectly formatted, I created this monstrosity. It compiles to a console application that’s included in my solution, one that searches back a couple of directories and formats all the source code files that it finds according to my drug-induced maniacal specifications. That is, it modifies hundreds of my source code files in parallel in a few milliseconds. By some miracle it works. Had it failed, the results would have been spectacular.

(Here’s the kicker… It doesn’t really format anything. The tool I used for formatting code inserted blank lines and omitted blank lines where I wanted extra whitespace. So all this does is insert and remove empty lines. So it parses files and determines where to insert and where to delete lines. That’s why it had the potential, if my calculations while coding when I was super-high, of deleting source code and making the files useless. But somehow it worked as intended.)

I can’t even understand this code while I am clean and sober (and sane).

There are some ridiculously long logic statements in this code. Also check out my essay-style comments. As I recall, I was explaining the code to the voices in my head.

The main file:

using System;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FormatCSFiles
{
    /// <summary>Some quick 'n dirty CS code formatting.</summary>
    /// <remarks>Because the RegionErate Addin that I use to sort my code into regions does some screwy
    /// things with blank lines, and I am obsessively fussy about my code format. This processes all CS
    /// files in the solution in parallel and fixes what it can. This is not an extension. I don't know
    /// how to write one. This processes <b>files</b>, so save all before running this in the debugger,
    /// or wave your latest changes goodbye.</remarks>
    internal class Program : Romy.Core.Console.ConsoleApplicationBase
    {
        [STAThread, SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
        private static void Main()
        {
            var program = new Program();
            program.InitializeConsoleApplication();

            /* I'm being lazy and assuming this project is always in a child directory of my solution and processing the parent directory
             * (+ all the parent's siblings, as well as children, which includes this file). i.e. 4 levels up from the Debug directory. */
            var directory = Environment.GetCommandLineArgs()[0];

            for (var i = 0; i < 4; i++)
            {
                directory = Path.GetDirectoryName(directory);
            }

            LogInfo("FormatCSFiles\tProcessing the .CS files in \"{0}\" and its subdirectories.", directory);

            var files = Directory.GetFiles(directory, "*.cs", SearchOption.AllDirectories).Where(
                f => !Path.GetFileName(f).StartsWith("TemporaryGeneratedFile"));

            Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, f => ProcessFile(directory, f));

            LogInfo("FormatCSFiles\tDone processing the .CS files in \"{0}\" and its subdirectories.", directory);
        }

        private static string Pad(string textToPad, int length)
        {
            var builder = new StringBuilder();
            builder.Append(textToPad);
            while (builder.Length < length)
            {
                builder.Append(' ');
            }
            return builder.ToString();
        }

        // My one and only failed attempt at reducing the code complexity made it even more complex.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static void ProcessFile(string directory, string filename)
        {
            try
            {
                var formattedThreadId = Thread.CurrentThread.ManagedThreadId.ToString().PadLeft(2, '0');
                var formattedFilename = filename.Substring(directory.Length + 1);
                LogInfo(Pad(string.Format("Thread {0}", formattedThreadId), "FormatCSFiles".Length) + string.Format("\tProcessing \"{0}\"", formattedFilename));

                var lines = File.ReadAllLines(filename, Encoding.Default).ToList();

                for (var i = lines.Count - 1; i >= 0; i--)
                {
                    var results = new AnalysisResults();
                    var line = lines[i].Trim();

                    if (i < lines.Count - 1)
                    {
                        var nextLine = lines[i + 1].Trim();
                        var lineIsEmpty = string.IsNullOrEmpty(line);
                        var nextLineIsEmpty = string.IsNullOrEmpty(nextLine);
                        var lineAfterNext = i < lines.Count - 2 ? lines[i + 2].Trim() : string.Empty;

                        results.RemoveNext = (!lineIsEmpty && line.StartsWith("[STAThread") && nextLineIsEmpty) || // Remove blank line below STAThread attribute.
                            // Remove a blank line between a property getter and setter.
                            (!lineIsEmpty && nextLineIsEmpty && line.StartsWith("get") && i < lines.Count + 2 && !string.IsNullOrEmpty(lineAfterNext) &&
                            (lineAfterNext.StartsWith("set") || lineAfterNext.StartsWith("private set") || lineAfterNext.StartsWith("protected set") ||
                            lineAfterNext.StartsWith("internal set") || lineAfterNext.StartsWith("public set"))) ||
                            (!lineIsEmpty && line.StartsWith("[") && nextLineIsEmpty && !string.IsNullOrEmpty(lineAfterNext)) ||
                            (!lineIsEmpty && line.StartsWith("{") && nextLineIsEmpty) ||
                            (!lineIsEmpty && line.StartsWith("[STAThread]") && nextLineIsEmpty);

                        if (!results.RemoveNext)
                        {
                            results.RemoveCurrent = (lineIsEmpty && nextLineIsEmpty) ||      // Remove extra blank line.
                                (i == 0 && lineIsEmpty) ||                                   // Remove empty first line.
                                (lineIsEmpty && !nextLineIsEmpty && nextLine.EndsWith("}")) || // Remove blank line before closing brace.
                                (lineIsEmpty && !nextLineIsEmpty && nextLine.StartsWith("static void Main("));

                            if (!results.RemoveCurrent)
                            {
                                results.InsertNext = (!lineIsEmpty && nextLine.StartsWith("static") && !nextLine.StartsWith("static void Main(") && nextLine.EndsWith("()")) || // Add a blank line before a static constructor.
                                    ((line.EndsWith("}") || line.EndsWith(";")) && nextLine.StartsWith("[")) || // Insert blank lines between consecutively declared structs. (Regionerate removed them.)
                                    (line.EndsWith(";") && (nextLine.StartsWith("public") || nextLine.StartsWith("private") ||
                                    nextLine.StartsWith("internal") || nextLine.StartsWith("protected") || nextLine.StartsWith("~"))) || // Insert blank lines between class-level fields.
                                    (!lineIsEmpty && line.StartsWith("#endregion") && !nextLineIsEmpty &&
                                    !nextLine.StartsWith("{") && !nextLine.StartsWith("}")) || // Insert a blank line between two subregions and anything else that doesn't start with an opening brace.
                                    (!lineIsEmpty && !nextLineIsEmpty && line.StartsWith("}") && (nextLine.StartsWith("#endregion") || nextLine.StartsWith("#endif"))) || // Insert a blank line between a closing brace and an #endregion or #endif.
                                    (!lineIsEmpty && line.StartsWith("#region") && !nextLineIsEmpty && nextLine.StartsWith("[")) || // Insert a blank line between an #endregion and an attribute.
                                    (!lineIsEmpty && (line.StartsWith("#region") || line.StartsWith("#if")) && !nextLineIsEmpty) || // Add blank line after #region or #pragma or #if.
                                    (!nextLine.StartsWith("private set") &&
                                    line.EndsWith("}") && !line.StartsWith("///") &&
                                    (nextLine.StartsWith("public") || nextLine.StartsWith("internal") || nextLine.StartsWith("private") ||
                                    nextLine.StartsWith("protected") || nextLine.StartsWith("///") || nextLine.StartsWith("async"))) || // Insert a line between a closing brace and a method declaration.
                                    (!lineIsEmpty && !nextLineIsEmpty && nextLine.Length > 1 && line.Length > 2 &&
                                    line.Contains('{') && line.EndsWith("}") && !line.StartsWith("get {") && !line.StartsWith("catch") &&
                                    !line.StartsWith("try") && !line.StartsWith("finally")) ||                                         // Insert a blank line between properties declared in an interface.
                                    (!lineIsEmpty && line.Length > 1 && !line.StartsWith("///") && (nextLine.StartsWith("///") || nextLine.StartsWith("/*"))) || // Insert a blank line between a field and method declaration.
                                    (!lineIsEmpty && !nextLineIsEmpty && line.StartsWith("using") && nextLine.StartsWith("namespace")) || // Add a blank line between a using clause and namespace name
                                    (!lineIsEmpty && !nextLineIsEmpty && line.StartsWith("private") && nextLine.StartsWith("#endregion") ||
                                    !lineIsEmpty && !nextLineIsEmpty && nextLine.StartsWith("#endregion"));

                                if (!results.InsertNext)
                                {
                                    if (!lineIsEmpty && line.EndsWith("</summary>") && !nextLineIsEmpty && nextLine.EndsWith(";") && !nextLine.StartsWith("event") && !string.IsNullOrEmpty(lines[i + 2].Trim()))
                                    {
                                        /* If a field declaration has a summary XML comment, add a blank
                                         * line after it as well. It makes the code easier to follow. */
                                        results.InsertAfterNext = true;
                                    }
                                    else if (!lineIsEmpty && line.EndsWith("{") && !nextLineIsEmpty && nextLine.Equals("}"))
                                    {
                                        // I hate javascript style braces. Just because.
                                        lines[i] += nextLine;

                                        results.RemoveNext = true;
                                    }
                                }
                            }
                        }

                        if (results.RemoveCurrent)
                            lines.RemoveAt(i);
                        else if (results.RemoveNext)
                            lines.RemoveAt(i + 1);
                        else if (results.InsertNext)
                            lines.Insert(i + 1, string.Empty);
                        else if (results.InsertAfterNext)
                            lines.Insert(i + 2, string.Empty);
                    }
                }

                /* It seems that using any encoding other than Encoding.UTF8, including my failed attempts
                 * to detect it, end up encoding the text in such a way that the hidden unicode characters
                 * that Regionerate uses in its region names, get scrambled. */
                File.WriteAllLines(filename, lines, Encoding.UTF8);
                LogInfo(Pad(string.Format("Thread {0}", formattedThreadId), "FormatCSFiles".Length) + string.Format("\tSaved \"{0}\"", formattedFilename));
            }
            catch (Exception ex) { LogError(ex); }
        }

        private class AnalysisResults
        {
            public bool InsertAfterNext { get; set; }

            public bool InsertNext { get; set; }

            public bool RemoveCurrent { get; set; }

            public bool RemoveNext { get; set; }
        }
    }
}

And my base console application class that it derived from (which isn’t too bad).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Romy.Core.Console
{
    /// <summary>A base console type that enables some logging methods. Logs
    /// via TraceListener, to both the console and a file.</summary><remarks>
    /// See Zipper\Program.cs or FormatCSFiles\Program.cs for examples.</remarks>
    public class ConsoleApplicationBase
    {
        private string logFilename;

        /// <summary>Delete the file or directory specified,
        /// without throwing an exception on failure.</summary>
        public static bool DeleteFileSystemObject(string path)
        {
            try
            {
                /* File.Exists seems to return true for directories,
                 * so always check with Directory.Exists first... */
                if (Directory.Exists(path))
                {
                    var info = new DirectoryInfo(path);

                    // If path == current directory, change the current directory.
                    if (string.Compare(Directory.GetCurrentDirectory(), path, StringComparison.InvariantCultureIgnoreCase) == 0 && Directory.Exists(Path.GetDirectoryName(path)))
                        Directory.SetCurrentDirectory(Path.GetDirectoryName(path));

                    // Try to silently delete readonly/hidden directories.
                    if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly || (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                        info.Attributes = FileAttributes.Directory & FileAttributes.Archive;

                    Directory.Delete(path, true);
                }
                else if (File.Exists(path))
                {
                    // Try to silently delete readonly/hidden files.
                    if ((File.GetAttributes(path) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly || (File.GetAttributes(path) & FileAttributes.Hidden) == FileAttributes.Hidden)
                        File.SetAttributes(path, FileAttributes.Normal);

                    File.Delete(path);
                }
                return true;
            }
            catch (IOException) { }
            catch (UnauthorizedAccessException) { }
            catch (Exception ex) { LogError(ex); }
            return false;
        }

        public static void LogError(Exception ex)
        {
            LogInfo("Error: {0}", ex.Message);
        }

        public static void LogError(string message)
        {
            LogInfo(message);
        }

        public static void LogInfo(IEnumerable<string> lines)
        {
            foreach (var line in lines)
            {
                Trace.WriteLine(line);
            }
        }

        public static void LogInfo(string format, params object[] args)
        {
            var formattedDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ff\t");
            Trace.WriteLine(formattedDate + (args.Length > 0 ? string.Format(format, args) : format));
        }

        /// <summary>This must be the first method called from Main,
        /// to be able to use the logging helper methods.</summary>
        public void InitializeConsoleApplication()
        {
            /* I prefer removing the parameters from Main and using this technique. It is always
             * consistent; the path to this exe is the first argument, and subsequent arguments
             * were passed by the user. */
            var args = Environment.GetCommandLineArgs();

            logFilename = Path.ChangeExtension(args[0], ".log");

            InitializeTraceListeners(logFilename);
        }

        private static void InitializeTraceListeners(string logFilename)
        {
            DeleteFileSystemObject(logFilename);

            Trace.Listeners.Clear();

            TextWriterTraceListener logFileTraceListener = new TextWriterTraceListener(logFilename);
            logFileTraceListener.Name = "FileLogger";

            ConsoleTraceListener consoleTraceListener = new ConsoleTraceListener(false);
            consoleTraceListener.Name = "ConsoleLogger";

            Trace.Listeners.Add(logFileTraceListener);
            Trace.Listeners.Add(consoleTraceListener);
            Trace.AutoFlush = true;
        }
    }
}
Advertisements

About Jerome

I am a senior C# developer in Johannesburg, South Africa. I am also a recovering addict, who spent nearly eight years using methamphetamine. I write on my recovery blog about my lessons learned and sometimes give advice to others who have made similar mistakes, often from my viewpoint as an atheist, and I also write some C# programming articles on my programming blog.
This entry was posted in Methamphetamine, Programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s