using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; namespace Differ { public class Folder { public Folder() { Name = ""; Path = ""; Parent = null; Files = new List(); Folders = new List(); } public Folder(string path, Folder Parent = null) { Name = (new DirectoryInfo(path).Name); Path = path; this.Parent = Parent; Files = new List(); Folders = new List(); ParseChilds(); } public string Name { get; private set; } public string Path { get; private set; } public Folder Parent { get; internal set; } public List Folders { get; private set; } public List Files { get; private set; } public void ParseChilds() { string[] directories = Directory.GetDirectories(Path); string[] files = Directory.GetFiles(Path); foreach (string file in files) { var f = new File(file, this); Files.Add(f); } foreach (string dir in directories) { var f = new Folder(dir, this); Folders.Add(f); } } public XmlNode GetXmlNode(XmlDocument document) { XmlNode node = document.CreateNode(XmlNodeType.Element, "Folder", ""); XmlAttribute nameAttribute = document.CreateAttribute("Name"); nameAttribute.InnerText = Name; node.Attributes.Append(nameAttribute); foreach (Folder folder in Folders) node.AppendChild(folder.GetXmlNode(document)); foreach (File file in Files) node.AppendChild(file.GetNode(document)); return node; } public bool Matches(Folder f) { // Only for this element, not for childs for now return Name == f.Name; } public string GetRelativePath() { Folder iterator = this; var builder = new StringBuilder(); while (iterator != null) { builder.Insert(0, iterator.Name); builder.Insert(0, '\\'); iterator = iterator.Parent; } return builder.ToString(); } public IEnumerable GetDifferences(Folder target) { List diffs = new List(); diffs.AddRange(GetDiffsForMissingFiles(target)); foreach (Folder folder in Folders) { Folder targetFolder = target.Folders.FirstOrDefault(f => f.Matches(folder)); if (targetFolder == null) { diffs.Add(new Difference(DiffType.MissingFolder, folder.GetRelativePath())); diffs.AddRange(folder.GetDiffsForMissingFolder()); } else diffs.AddRange(folder.GetDifferences(targetFolder)); } diffs.AddRange(target.Folders. Where(f => !Folders.Any(f2 => f2.Name == f.Name)). Select(folder => new Difference(DiffType.RedundantFolder, folder.GetRelativePath()))); return diffs; } private IEnumerable GetDiffsForMissingFiles(Folder target) { try { return from file in Files let targetFile = target.Files.FirstOrDefault(f => f.Matches(file)) where targetFile == default(File) select new Difference(DiffType.MissingFile, file.GetRelativePath(), file.Path); } catch (NullReferenceException) { return new List(); } catch (AggregateException) { return new List(); } } public IEnumerable GetDiffsForMissingFolder() { foreach (File file in Files) yield return new Difference(DiffType.MissingFile, file.GetRelativePath(), file.Path); foreach (Folder folder in Folders) { foreach (Difference diff in folder.GetDiffsForMissingFolder()) yield return diff; } } public void ParseFromXmlNode(XmlNode node) { foreach (XmlAttribute attribute in node.Attributes) { switch (attribute.Name) { case "Name": Name = attribute.InnerText; break; default: break; } foreach (XmlNode n in node.ChildNodes) { if (n.Name == "File") { var f = new File(); f.ParseFromXmlNode(n); } } } } #region Nested type: FolderParser internal class FolderParser { public FolderParser(Folder source, Folder target, Action AddAction) { Source = source; Target = target; this.AddAction = AddAction; Diffs = new List(); } public List Diffs { get; set; } public Folder Source { get; set; } public Folder Target { get; set; } public Action AddAction { get; set; } public void Run() { Diffs.Clear(); RunForFiles(); RunForFolders(); } private void RunForFolders() { if(Target == null) Target = new Folder(); IEnumerable redunantFolderDiffs = GetDiffsForRedunantFolders(); IEnumerable missingFolderDiffs = GetDiffsForMissingFolders(); IEnumerable diffsFromMissingFolders = GetDiffsFromMissingFolders(); Diffs.AddRange(redunantFolderDiffs); Diffs.AddRange(missingFolderDiffs); Diffs.AddRange(diffsFromMissingFolders); foreach (var pair in GetFolderPairs()) AddAction(new FolderParser(pair.Key, pair.Value, AddAction)); } private IEnumerable GetDiffsForRedunantFolders() { try { return (from targetFolder in Target.Folders let sourceFolder = Source.Folders.FirstOrDefault(sf => sf.Name == targetFolder.Name) where sourceFolder == null select new Difference(DiffType.RedundantFolder, targetFolder.GetRelativePath())); } catch (NullReferenceException) { return new List(); } catch (AggregateException) { return new List(); } } private IEnumerable GetDiffsForMissingFolders() { try { return (from sourceFolder in Source.Folders let targetFolder = Target.Folders.FirstOrDefault(tf => tf.Name == sourceFolder.Name) where targetFolder == null select new Difference(DiffType.MissingFolder, sourceFolder.GetRelativePath())); } catch (NullReferenceException) { return new List(); } catch (AggregateException) { return new List(); } } private IEnumerable GetDiffsFromMissingFolders() { try { return (from sourceFolder in Source.Folders let targetFolder = Target.Folders.FirstOrDefault(tf => tf.Name == sourceFolder.Name) where targetFolder == null from diff in sourceFolder.GetDiffsForMissingFolder() select diff); } catch (NullReferenceException) { return new List(); } catch (AggregateException) { return new List(); } } private IEnumerable> GetFolderPairs() { try { return (from sourceFolder in Source.Folders let targetFolder = Target.Folders.FirstOrDefault(tf => tf.Matches(sourceFolder)) where targetFolder != null select new KeyValuePair(sourceFolder, targetFolder)); } catch (NullReferenceException) { return new List>(); } catch (AggregateException) { return new List>(); } } private void RunForFiles() { // Select changed or missing files if(Target == null) Target = new Folder(); List tmp = new List(); //if (Target != null) { tmp.AddRange(GetDiffsForMissingFiles()); // Select redunant files tmp.AddRange(from file in Target.Files let sourceFile = Source.Files.FirstOrDefault(f => f.Name == file.Name) where sourceFile == null select new Difference(DiffType.RedundantFile, file.GetRelativePath())); //} Diffs.AddRange(tmp); } private IEnumerable GetDiffsForMissingFiles() { try { return from file in Source.Files let targetFile = Target.Files.FirstOrDefault(f => f.Matches(file)) where targetFile == null select new Difference(DiffType.MissingFile, file.GetRelativePath(), file.Path); } catch (NullReferenceException) { if (Source == null || Source.Files == null) { return new List(); } return from file in Source.Files select new Difference(DiffType.MissingFile, file.GetRelativePath(), file.Path); } catch (AggregateException) { if (Source == null || Source.Files == null) { return new List(); } return from file in Source.Files select new Difference(DiffType.MissingFile, file.GetRelativePath(), file.Path); } } } #endregion } }