using System; using System.Collections.Generic; using System.IO; using System.Linq; using DCIT.Core.Models; namespace DCIT.Core.Services { public sealed class BatchExecutionPlanner { public BatchExecutionPlan CreatePlan(IList selectedItems, int processorCount) { var executionItems = new List(); var skippedItems = new List(); var sourcePaths = new HashSet(StringComparer.OrdinalIgnoreCase); var outputPaths = new HashSet(StringComparer.OrdinalIgnoreCase); var diffPaths = new HashSet(StringComparer.OrdinalIgnoreCase); if (selectedItems != null) { foreach (var item in selectedItems) { if (item == null) { continue; } var sourcePath = NormalizePath(item.SourcePath); var outputPath = NormalizePath(item.OutputPath); var diffPath = NormalizePath(item.DiffPath); var collision = GetCollision(sourcePath, sourcePaths, BatchExecutionSkipReason.DuplicateSourcePath); if (collision == null) { collision = GetCollision(outputPath, outputPaths, BatchExecutionSkipReason.DuplicateOutputPath); } if (collision == null && !string.IsNullOrWhiteSpace(diffPath)) { collision = GetCollision(diffPath, diffPaths, BatchExecutionSkipReason.DuplicateDiffPath); } if (collision != null) { collision.Item = item; skippedItems.Add(collision); continue; } AddIfPresent(sourcePaths, sourcePath); AddIfPresent(outputPaths, outputPath); AddIfPresent(diffPaths, diffPath); executionItems.Add(item); } } var requiresSerialExecution = executionItems.Any(item => Path.GetExtension(item.SourcePath).Equals(".doc", StringComparison.OrdinalIgnoreCase)); var degreeOfParallelism = DetermineDegreeOfParallelism(executionItems.Count, processorCount, requiresSerialExecution); return new BatchExecutionPlan { ExecutionItems = executionItems, SkippedItems = skippedItems, DegreeOfParallelism = degreeOfParallelism, RequiresSerialExecution = requiresSerialExecution, }; } private static BatchExecutionSkipItem GetCollision(string path, HashSet existingPaths, BatchExecutionSkipReason reason) { if (string.IsNullOrWhiteSpace(path) || !existingPaths.Contains(path)) { return null; } return new BatchExecutionSkipItem { Reason = reason, ConflictingPath = path, ExistingPath = path, }; } private static void AddIfPresent(HashSet set, string path) { if (!string.IsNullOrWhiteSpace(path)) { set.Add(path); } } private static int DetermineDegreeOfParallelism(int itemCount, int processorCount, bool requiresSerialExecution) { if (itemCount <= 0 || requiresSerialExecution) { return 1; } var normalizedProcessorCount = processorCount > 0 ? processorCount : Environment.ProcessorCount; normalizedProcessorCount = Math.Max(1, normalizedProcessorCount); return Math.Max(1, Math.Min(itemCount, normalizedProcessorCount)); } private static string NormalizePath(string path) { if (string.IsNullOrWhiteSpace(path)) { return string.Empty; } try { return Path.GetFullPath(path) .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); } catch { return path.Trim(); } } } }