コード例 #1
0
ファイル: PreprocessXml.cs プロジェクト: bakx/code2yaml
        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);
        }
コード例 #2
0
        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));
        }
コード例 #3
0
ファイル: GenerateArticles.cs プロジェクト: viagrif/code2yaml
        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);
                }
            });
        }