// ---------------- Functions ----------------

        public void Run(IEnumerable <FilePath> files, CakeLicenseHeaderUpdaterSettings settings)
        {
            if (files == null)
            {
                throw new ArgumentNullException(nameof(files));
            }

            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }

            settings = FixupSettings(settings);

            ConcurrentQueue <FilePath>       filesToProcess = new ConcurrentQueue <FilePath>(files);
            List <FileProcessorRunner>       runners        = new List <FileProcessorRunner>(settings.Threads);
            List <FileProcessorRunnerResult> results        = new List <FileProcessorRunnerResult>(settings.Threads);

            using (this.context.Log.WithVerbosity(settings.Verbosity))
            {
                for (int i = 1; i <= settings.Threads; ++i)
                {
                    FileProcessorRunner runner = new FileProcessorRunner(i, this.context.Log, filesToProcess, settings);
                    runners.Add(runner);
                }

                try
                {
                    foreach (FileProcessorRunner runner in runners)
                    {
                        runner.Start();
                    }

                    foreach (FileProcessorRunner runner in runners)
                    {
                        results.Add(runner.Wait());
                    }
                }
                finally
                {
                    foreach (FileProcessorRunner runner in runners)
                    {
                        try
                        {
                            runner.Dispose();
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }

            CakeLicenseHeaderUpdaterRunnerException exception = new CakeLicenseHeaderUpdaterRunnerException(results);

            if (exception.InnerExceptions.Count != 0)
            {
                throw exception;
            }
        }
        public static void UpdateLicenseHeaders(
            this ICakeContext context,
            IEnumerable <FilePath> files,
            CakeLicenseHeaderUpdaterSettings settings
            )
        {
            CakeLicenseHeaderUpdaterRunner runner = new CakeLicenseHeaderUpdaterRunner(context);

            runner.Run(files, settings);
        }
        // ---------------- Constructor ----------------

        public FileProcessorRunner(
            int threadNumber,
            ICakeLog log,
            ConcurrentQueue <FilePath> files,
            CakeLicenseHeaderUpdaterSettings settings
            )
        {
            this.threadNumber = threadNumber;
            this.isDisposed   = false;
            this.log          = log;
            this.files        = files;
            this.settings     = settings;

            this.processor = new FileProcessor(this.threadNumber, this.log, this.settings);
            this.result    = new FileProcessorRunnerResult();
        }
        internal static CakeLicenseHeaderUpdaterSettings FixupSettings(CakeLicenseHeaderUpdaterSettings settings)
        {
            CakeLicenseHeaderUpdaterSettings realSettings = new CakeLicenseHeaderUpdaterSettings();

            realSettings.DryRun = settings.DryRun;

            if (settings.FileFilter == null)
            {
                realSettings.FileFilter = ( FilePath path ) => true;
            }
            else
            {
                realSettings.FileFilter = settings.FileFilter;
            }

            if (string.IsNullOrEmpty(settings.LicenseString))
            {
                realSettings.LicenseString = null;
            }
            else
            {
                realSettings.LicenseString = settings.LicenseString;
            }

            IEnumerable <string> fixedRegexes = settings.OldHeaderRegexPatterns.Where(
                s => string.IsNullOrEmpty(s) == false
                );

            foreach (string s in fixedRegexes)
            {
                realSettings.OldHeaderRegexPatterns.Add(s);
            }

            if (settings.Threads <= 0)
            {
                realSettings.Threads = Environment.ProcessorCount;
            }
            else
            {
                realSettings.Threads = settings.Threads;
            }

            realSettings.Verbosity = settings.Verbosity;

            return(realSettings);
        }
        // ---------------- Constructor ----------------

        public FileProcessor(int threadNumber, ICakeLog log, CakeLicenseHeaderUpdaterSettings settings)
        {
            this.threadNumber = threadNumber;
            this.log          = log;
            this.settings     = settings;

            StringBuilder builder = new StringBuilder();

            foreach (string pattern in settings.OldHeaderRegexPatterns)
            {
                builder.Append($"({pattern})|");
            }

            if (settings.OldHeaderRegexPatterns.Count > 0)
            {
                builder.Remove(builder.Length - 1, 1);
            }

            this.oldRegexes = new Regex(
                builder.ToString(),
                RegexOptions.Compiled | RegexOptions.ExplicitCapture
                );
        }