public async Task RunAsync(BuildContext context) { var config = context.GetSharedObject(Constants.Config) as ConfigModel; if (config == null) { throw new ApplicationException(string.Format("Key: {0} doesn't exist in build context", Constants.Config)); } string inputPath = StepUtility.GetDoxygenXmlOutputPath(config.OutputPath); var processedOutputPath = StepUtility.GetProcessedXmlOutputPath(config.OutputPath); if (Directory.Exists(processedOutputPath)) { Directory.Delete(processedOutputPath, recursive: true); } var dirInfo = Directory.CreateDirectory(processedOutputPath); // workaround for Doxygen Bug: it generated xml whose encoding is ANSI while the xml meta is encoding='UTF-8' // preprocess in string level: fix style for type with template parameter Directory.EnumerateFiles(inputPath, "*.xml").AsParallel().ForAll( p => { var content = File.ReadAllText(p, Encoding.UTF8); content = TemplateLeftTagRegex.Replace(content, "$1"); content = TemplateRightTagRegex.Replace(content, "$1"); XDocument doc = XDocument.Parse(content); doc.Save(p); }); // get friendly uid var uidMapping = new ConcurrentDictionary <string, string>(); var compounddefIdMapping = new ConcurrentDictionary <string, string>(); await Directory.EnumerateFiles(inputPath, "*.xml").ForEachInParallelAsync( p => { XDocument doc = XDocument.Load(p); var def = doc.Root.Element("compounddef"); var formatedCompoundDefId = string.Empty; if (def != null) { if (KindToDeletedCollection.Contains(def.Attribute("kind").Value)) { File.Delete(p); return(Task.FromResult(1)); } var id = def.Attribute("id").Value; formatedCompoundDefId = def.Element("compoundname").Value.Replace(Constants.NameSpliter, Constants.IdSpliter); uidMapping[id] = formatedCompoundDefId; compounddefIdMapping[id] = formatedCompoundDefId; } foreach (var node in doc.XPathSelectElements("//memberdef[@id]")) { var id = node.Attribute("id").Value; uidMapping[id] = PreprocessMemberUid(node, formatedCompoundDefId); } return(Task.FromResult(1)); }); // workaround for Doxygen Bug: it generated extra namespace for code `public string namespace(){ return ""; }`. // so if we find namespace which has same name with class, remove it from index file and also remove its file. string indexFile = Path.Combine(inputPath, Constants.IndexFileName); XDocument indexDoc = XDocument.Load(indexFile); var duplicateItems = (from ele in indexDoc.Root.Elements("compound") let uid = (string)ele.Attribute("refid") group ele by RegularizeUid(uid) into g let duplicate = g.FirstOrDefault(e => (string)e.Attribute("kind") == "namespace") where g.Count() > 1 && duplicate != null select(string) duplicate.Attribute("refid")).ToList(); // Get duplicate Ids when ignore case var results = duplicateItems.Where(id => compounddefIdMapping.ContainsKey(id)).Select(k => compounddefIdMapping.TryRemove(k, out _)).ToList(); var duplicatedIds = compounddefIdMapping.GroupBy(k => k.Value.ToLower()) .Where(g => g.Count() > 1) .Select(kg => kg.Select(kv => kv.Key)) .SelectMany(ke => ke).ToList(); var extendedIdMaping = new ConcurrentDictionary <string, string>(); await Directory.EnumerateFiles(inputPath, "*.xml").ForEachInParallelAsync( p => { XDocument doc = XDocument.Load(p); if (Path.GetFileName(p) == Constants.IndexFileName) { var toBeRemoved = (from item in duplicateItems select doc.XPathSelectElement($"//compound[@refid='{item}']")).ToList(); foreach (var element in toBeRemoved) { element.Remove(); } } else if (duplicateItems.Contains(Path.GetFileNameWithoutExtension(p))) { return(Task.FromResult(1)); } else { // workaround for Doxygen Bug: https://bugzilla.gnome.org/show_bug.cgi?id=710175 // so if we find package section func/attrib, first check its type, if it starts with `public` or `protected`, move it to related section var toBeMoved = new Dictionary <string, List <XElement> >(); var packageMembers = doc.XPathSelectElements("//memberdef[@prot='package']").ToList(); foreach (var member in packageMembers) { string kind = (string)member.Parent.Attribute("kind"); var type = member.Element("type"); string regulized, access; if (type != null && TryRegularizeReturnType(type.CreateNavigator().InnerXml, out regulized, out access)) { if (regulized == string.Empty) { type.Remove(); } else { type.ReplaceWith(XElement.Parse($"<type>{regulized}</type>")); } member.Attribute("prot").Value = access; var belongToSection = GetSectionKind(access, kind); List <XElement> elements; if (!toBeMoved.TryGetValue(belongToSection, out elements)) { elements = new List <XElement>(); toBeMoved[belongToSection] = elements; } elements.Add(member); member.Remove(); } } foreach (var pair in toBeMoved) { var section = doc.XPathSelectElement($"//sectiondef[@kind='{pair.Key}']"); if (section == null) { section = new XElement("sectiondef", new XAttribute("kind", pair.Key)); doc.Root.Element("compounddef").Add(section); } foreach (var c in pair.Value) { section.Add(c); } } } foreach (var node in doc.XPathSelectElements("//node()[@refid]")) { node.Attribute("refid").Value = RegularizeUid(node.Attribute("refid").Value, uidMapping); } foreach (var node in doc.XPathSelectElements("//node()[@id]")) { node.Attribute("id").Value = RegularizeUid(node.Attribute("id").Value, uidMapping); } // remove copyright comment foreach (var node in doc.XPathSelectElements("//para").ToList()) { if (CopyRightCommentCollection.Contains(node.Value.Trim())) { node.Remove(); } } string fileName = Path.GetFileNameWithoutExtension(p); if (compounddefIdMapping.TryGetValue(fileName, out string formatedFileName)) { formatedFileName = RegularizeUid(formatedFileName); if (duplicatedIds.Contains(fileName)) { fileName = string.Format(Constants.RenamedFormat, formatedFileName, TryGetType(fileName)); extendedIdMaping[formatedFileName] = fileName; } else { fileName = formatedFileName; } } doc.Save(Path.Combine(dirInfo.FullName, fileName + Path.GetExtension(p))); return(Task.FromResult(1)); }); context.SetSharedObject(Constants.ExtendedIdMappings, extendedIdMaping); }
public Task RunAsync(BuildContext context) { var config = context.GetSharedObject(Constants.Config) as ConfigModel; if (config == null) { throw new ApplicationException(string.Format("Key: {0} doesn't exist in build context", Constants.Config)); } var extendedIdMappings = context.GetSharedObject(Constants.ExtendedIdMappings) as ConcurrentDictionary <string, string>; string inputPath = StepUtility.GetProcessedXmlOutputPath(config.OutputPath); // Parse Index file string indexFile = Path.Combine(inputPath, Constants.IndexFileName); using (var stream = File.OpenRead(indexFile)) { XDocument doc = XDocument.Load(stream); _changeDict = (from ele in doc.Root.Elements("compound") let uid = (string)ele.Attribute("refid") let type = ParseType((string)ele.Attribute("kind")) where uid != null && type.HasValue select new HierarchyChange { Uid = uid, Name = (string)ele.Element("name"), File = extendedIdMappings.ContainsKey(uid) ? extendedIdMappings[uid] + Constants.XmlExtension : uid + Constants.XmlExtension, Type = type.Value, }).ToDictionary(c => c.Uid); } // Parse File to get parent/children info and package-private/private items // assume that couldn't define public class inside package-private/private class var itemsToRemove = new List <string>(); foreach (var pair in _changeDict) { using (var stream = File.OpenRead(Path.Combine(inputPath, pair.Value.File))) { HashSet <string> children = new HashSet <string>(); string parent = pair.Key; XDocument doc = XDocument.Load(stream); var def = doc.Root.Element("compounddef"); if (def == null) { throw new ApplicationException(string.Format("there is no compounddef section for {0}", parent)); } // filter out package-private item var prot = (string)def.Attribute("prot"); if (YamlUtility.IsFiltered(prot)) { itemsToRemove.Add(pair.Key); // add innerclass because Doxygen would still output nested public classes var inner = def.Elements("innerclass").Select(i => (string)i.Attribute("refid")); itemsToRemove.AddRange(inner); continue; } // check innerclass's access label because Doxygen would still output nested private/package-private classes var innerClasses = def.Elements("innerclass").Where(e => !YamlUtility.IsFiltered((string)e.Attribute("prot"))); foreach (var inner in innerClasses) { string innerId = (string)inner.Attribute("refid"); HierarchyChange change; if (innerId == null || !_changeDict.TryGetValue(innerId, out change)) { throw new ApplicationException(string.Format("Inner {0} isn't in change dict.", innerId)); } change.Parent = parent; children.Add(innerId); } pair.Value.Children = children; } } foreach (var key in itemsToRemove) { _changeDict.Remove(key); } // remove namespace that is empty and update its parent var dict = new Dictionary <string, HierarchyChange>(_changeDict); foreach (var change in from c in _changeDict.Values where c.Type == HierarchyType.Namespace orderby c.Children.Count select c) { if (change.Children.Count() == 0) { if (!dict.Remove(change.Uid)) { throw new ApplicationException(string.Format("fail to remove empty namespace change: {0}", change.Uid)); } if (change.Parent != null) { dict[change.Parent].Children.Remove(change.Uid); } } } _changeDict = dict; // update innerclass's parent to its outerclass's parent recursively until namespace foreach (var pair in _changeDict) { string parent = pair.Value.Parent; string originalParent = parent; while (parent != null) { var parentChange = _changeDict[parent]; if (parentChange.Type == HierarchyType.Namespace) { pair.Value.Parent = parent; if (originalParent != parent) { _changeDict[parent].Children.Add(pair.Key); _changeDict[originalParent].Children.Remove(pair.Key); } break; } parent = parentChange.Parent; } } context.SetSharedObject(Constants.Changes, _changeDict); return(Task.FromResult(1)); }
public async Task RunAsync(BuildContext context) { var config = context.GetSharedObject(Constants.Config) as ConfigModel; if (config == null) { throw new ApplicationException(string.Format("Key: {0} doesn't exist in build context", Constants.Config)); } string inputPath = StepUtility.GetProcessedXmlOutputPath(config.OutputPath); string outputPath = config.OutputPath; var changesDict = context.GetSharedObject(Constants.Changes) as Dictionary <string, HierarchyChange>; if (changesDict == null) { throw new ApplicationException(string.Format("Key: {0} doesn't exist in build context", Constants.Changes)); } var infoDict = new ConcurrentDictionary <string, ArticleItemYaml>(); context.SetSharedObject(Constants.ArticleItemYamlDict, infoDict); var pages = await changesDict.Values.SelectInParallelAsync( async change => { using (var input = File.OpenRead(Path.Combine(inputPath, change.File))) { XDocument doc = XDocument.Load(input); var cloned = context.Clone(); cloned.SetSharedObject(Constants.CurrentChange, change); HierarchyChange parent = change.Parent != null ? changesDict[change.Parent] : null; cloned.SetSharedObject(Constants.ParentChange, parent); IArticleGenerator generator = (IArticleGenerator)Generator.Clone(); PageModel page = await generator.GenerateArticleAsync(cloned, doc); foreach (var item in page.Items) { if (!infoDict.TryAdd(item.Uid, item)) { ConsoleLogger.WriteLine( new LogEntry { Phase = StepName, Level = LogLevel.Warning, Message = $"Duplicate items {item.Uid} found in {change.File}.", }); } } return(page); } }); // update type declaration/reference and save yaml await pages.ForEachInParallelAsync( async page => { // update declaration var cloned = context.Clone(); await Generator.PostGenerateArticleAsync(cloned, page); // update reference foreach (var reference in page.References) { ArticleItemYaml yaml; if (infoDict.TryGetValue(reference.Uid, out yaml)) { reference.Name = yaml.Name; reference.Type = yaml.Type; reference.NameWithType = yaml.NameWithType; reference.FullName = yaml.FullName; reference.Href = yaml.Href; reference.Parent = yaml.Parent; reference.Syntax = yaml.Syntax; reference.Summary = yaml.Summary; } else if (reference.SpecForJava != null) { foreach (var spec in reference.SpecForJava) { if (spec.Uid != null) { var specYaml = infoDict[spec.Uid]; spec.Name = specYaml.NameWithoutTypeParameter ?? specYaml.Name; spec.FullName = specYaml.FullNameWithoutTypeParameter ?? specYaml.FullName; spec.Href = specYaml.Href; } } } } using (var writer = new StreamWriter(Path.Combine(outputPath, page.Items[0].Href))) { writer.WriteLine(Constants.YamlMime.ManagedReference); YamlSerializer.Value.Serialize(writer, page); } }); }