예제 #1
0
        /// <summary>
        /// Merge the conceptual topic IDs into the BuildAssembler manifest file.
        /// </summary>
        /// <param name="builder">The build process</param>
        private void MergeConceptualManifest(BuildProcess builder)
        {
            XmlWriterSettings settings           = new XmlWriterSettings();
            XmlWriter         writer             = null;
            string            conceptualManifest = builder.WorkingFolder + "ConceptualManifest.xml",
                              referenceManifest  = builder.WorkingFolder + "manifest.xml";

            builder.ReportProgress("    Merging topic IDs into manifest.xml");

            try
            {
                settings.Indent      = true;
                settings.CloseOutput = true;
                writer = XmlWriter.Create(conceptualManifest, settings);

                writer.WriteStartDocument();
                writer.WriteStartElement("topics");

                foreach (TopicCollection tc in topics)
                {
                    foreach (Topic t in tc)
                    {
                        t.WriteManifest(writer, builder);
                    }
                }

                if (File.Exists(referenceManifest))
                {
                    foreach (var topic in ComponentUtilities.XmlStreamAxis(referenceManifest, "topic"))
                    {
                        writer.WriteStartElement("topic");

                        foreach (var attr in topic.Attributes())
                        {
                            writer.WriteAttributeString(attr.Name.LocalName, attr.Value);
                        }

                        writer.WriteEndElement();
                    }
                }

                writer.WriteEndElement();   // </topics>
                writer.WriteEndDocument();
            }
            finally
            {
                if (writer != null)
                {
                    writer.Close();
                }

                if (File.Exists(referenceManifest))
                {
                    File.Copy(referenceManifest, Path.ChangeExtension(referenceManifest, "old"), true);
                }

                File.Copy(conceptualManifest, referenceManifest, true);
                File.Delete(conceptualManifest);
            }
        }
예제 #2
0
        //=====================================================================

        /// <summary>
        /// This is used to generate the API topic manifest
        /// </summary>
        private void GenerateApiTopicManifest()
        {
            this.ReportProgress(BuildStep.GenerateApiTopicManifest, "Generating API topic manifest...");

            if (!this.ExecutePlugIns(ExecutionBehaviors.InsteadOf))
            {
                this.ExecutePlugIns(ExecutionBehaviors.Before);

                using (XmlWriter writer = XmlWriter.Create(Path.Combine(this.WorkingFolder, "manifest.xml"),
                                                           new XmlWriterSettings {
                    Indent = true
                }))
                {
                    writer.WriteStartDocument();
                    writer.WriteStartElement("topics");

                    foreach (var api in ComponentUtilities.XmlStreamAxis(reflectionFile, "api"))
                    {
                        // Skip elements that should not have a topic generated (enumerated type fields for example)
                        if (api.Element("topicdata")?.Attribute("notopic") == null)
                        {
                            writer.WriteStartElement("topic");
                            writer.WriteAttributeString("id", api.Attribute("id").Value);
                            writer.WriteAttributeString("type", "API");
                            writer.WriteEndElement();
                        }
                    }

                    writer.WriteEndElement();
                }

                this.ExecutePlugIns(ExecutionBehaviors.After);
            }
        }
예제 #3
0
        //=====================================================================

        /// <summary>
        /// This is used to get an enumerable list of unique namespaces from the given reflection data file
        /// </summary>
        /// <param name="reflectionInfoFile">The reflection data file to search for namespaces</param>
        /// <param name="validNamespaces">An enumerable list of valid namespaces</param>
        /// <returns>An enumerable list of unique namespaces</returns>
        public static IEnumerable <string> GetReferencedNamespaces(string reflectionInfoFile,
                                                                   IEnumerable <string> validNamespaces)
        {
            HashSet <string> seenNamespaces = new HashSet <string>();
            string           ns;

            // Find all type references and extract the namespace from them.  This is a rather brute force way
            // of doing it but the type element can appear in various places.  This way we find them all.
            // Examples: ancestors/type/@api, returns/type/@api, parameter/type/@api,
            // parameter/referenceTo/type/@api, attributes/attribute/argument/type/@api,
            // returns/type/specialization/type/@api, containers/type/@api, overrides/member/type/@api
            var typeRefs = ComponentUtilities.XmlStreamAxis(reflectionInfoFile, "type").Select(
                el => el.Attribute("api").Value);

            foreach (string typeName in typeRefs)
            {
                if (typeName.Length > 2 && typeName.IndexOf('.') != -1)
                {
                    ns = typeName.Substring(2, typeName.LastIndexOf('.') - 2);

                    if (validNamespaces.Contains(ns) && !seenNamespaces.Contains(ns))
                    {
                        seenNamespaces.Add(ns);
                        yield return(ns);
                    }
                }
            }
        }
예제 #4
0
        /// <summary>
        /// This is used to get an enumerable list of unique namespaces referenced in the XML comments files of
        /// the given set of namespaces.
        /// </summary>
        /// <param name="language">The language to use when locating the XML comments files</param>
        /// <param name="searchNamespaces">An enumerable list of namespaces to search</param>
        /// <param name="validNamespaces">An enumerable list of valid namespaces</param>
        /// <returns>An enumerable list of unique namespaces in the related XML comments files</returns>
        public IEnumerable <string> GetReferencedNamespaces(CultureInfo language,
                                                            IEnumerable <string> searchNamespaces, IEnumerable <string> validNamespaces)
        {
            HashSet <string> seenNamespaces = new HashSet <string>();
            string           ns;

            foreach (string path in this.CommentsFileLocations(language))
            {
                foreach (string file in Directory.EnumerateFiles(Path.GetDirectoryName(path),
                                                                 Path.GetFileName(path)).Where(f => searchNamespaces.Contains(Path.GetFileNameWithoutExtension(f))))
                {
                    // Find all comments elements with a reference.  XML comments files may be ill-formed so
                    // ignore any elements without a cref attribute.
                    var crefs = ComponentUtilities.XmlStreamAxis(file, new[] { "event", "exception",
                                                                               "inheritdoc", "permission", "see", "seealso" }).Select(
                        el => (string)el.Attribute("cref")).Where(c => c != null);

                    foreach (string refId in crefs)
                    {
                        if (refId.Length > 2 && refId[1] == ':' && refId.IndexOfAny(new[] { '.', '(' }) != -1)
                        {
                            ns = refId.Trim();

                            // Strip off member name?
                            if (!ns.StartsWith("R:", StringComparison.OrdinalIgnoreCase) &&
                                !ns.StartsWith("G:", StringComparison.OrdinalIgnoreCase) &&
                                !ns.StartsWith("N:", StringComparison.OrdinalIgnoreCase) &&
                                !ns.StartsWith("T:", StringComparison.OrdinalIgnoreCase))
                            {
                                if (ns.IndexOf('(') != -1)
                                {
                                    ns = ns.Substring(0, ns.IndexOf('('));
                                }

                                if (ns.IndexOf('.') != -1)
                                {
                                    ns = ns.Substring(0, ns.LastIndexOf('.'));
                                }
                            }

                            if (ns.IndexOf('.') != -1)
                            {
                                ns = ns.Substring(2, ns.LastIndexOf('.') - 2);
                            }
                            else
                            {
                                ns = ns.Substring(2);
                            }

                            if (validNamespaces.Contains(ns) && !seenNamespaces.Contains(ns))
                            {
                                seenNamespaces.Add(ns);
                                yield return(ns);
                            }
                        }
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Merge the conceptual topic IDs into the BuildAssembler manifest file.
        /// </summary>
        /// <param name="builder">The build process</param>
        private void MergeConceptualManifest(BuildProcess builder)
        {
            string conceptualManifest = Path.Combine(builder.WorkingFolder, "ConceptualManifest.xml"),
                   referenceManifest  = Path.Combine(builder.WorkingFolder, "manifest.xml");

            builder.ReportProgress("    Merging topic IDs into manifest.xml");

            using (var writer = XmlWriter.Create(conceptualManifest, new XmlWriterSettings {
                Indent = true, CloseOutput = true
            }))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement("topics");

                foreach (TopicCollection tc in this.Topics)
                {
                    foreach (Topic t in tc)
                    {
                        t.WriteManifest(writer, builder);
                    }
                }

                if (File.Exists(referenceManifest))
                {
                    foreach (var topic in ComponentUtilities.XmlStreamAxis(referenceManifest, "topic"))
                    {
                        writer.WriteStartElement("topic");

                        foreach (var attr in topic.Attributes())
                        {
                            writer.WriteAttributeString(attr.Name.LocalName, attr.Value);
                        }

                        writer.WriteEndElement();
                    }
                }

                writer.WriteEndElement();
                writer.WriteEndDocument();
            }

            if (File.Exists(referenceManifest))
            {
                File.Copy(referenceManifest, Path.ChangeExtension(referenceManifest, "old"), true);
            }

            File.Copy(conceptualManifest, referenceManifest, true);
            File.Delete(conceptualManifest);
        }
예제 #6
0
        /// <summary>
        /// This is used to get an enumerable list of unique namespaces referenced in the topic
        /// </summary>
        /// <param name="validNamespaces">An enumerable list of valid framework namespaces</param>
        /// <returns>An enumerable list of unique namespaces in the topic</returns>
        public IEnumerable <string> GetReferencedNamespaces(IEnumerable <string> validNamespaces)
        {
            HashSet <string> seenNamespaces = new HashSet <string>();
            string           ns;

            // Find all code entity references
            var entityRefs = ComponentUtilities.XmlStreamAxis(contentFile.FullPath, "codeEntityReference").Select(el => el.Value);

            foreach (var refName in entityRefs)
            {
                if (refName.Length > 2 && refName.IndexOfAny(new[] { '.', '(' }) != -1)
                {
                    ns = refName.Trim();

                    // Strip off member name?
                    if (!ns.StartsWith("R:", StringComparison.OrdinalIgnoreCase) &&
                        !ns.StartsWith("G:", StringComparison.OrdinalIgnoreCase) &&
                        !ns.StartsWith("N:", StringComparison.OrdinalIgnoreCase) &&
                        !ns.StartsWith("T:", StringComparison.OrdinalIgnoreCase))
                    {
                        if (ns.IndexOf('(') != -1)
                        {
                            ns = ns.Substring(0, ns.IndexOf('('));
                        }

                        if (ns.IndexOf('.') != -1)
                        {
                            ns = ns.Substring(0, ns.LastIndexOf('.'));
                        }
                    }

                    if (ns.IndexOf('.') != -1)
                    {
                        ns = ns.Substring(2, ns.LastIndexOf('.') - 2);
                    }
                    else
                    {
                        ns = ns.Substring(2);
                    }

                    if (validNamespaces.Contains(ns) && !seenNamespaces.Contains(ns))
                    {
                        seenNamespaces.Add(ns);
                        yield return(ns);
                    }
                }
            }
        }
        /// <summary>
        /// This combines the conceptual and API intermediate TOC files into one file ready for transformation to
        /// the help format-specific TOC file formats and, if necessary, determines the default topic.
        /// </summary>
        private void CombineIntermediateTocFiles()
        {
            XmlAttribute attr;
            XmlDocument  conceptualXml = null, tocXml;
            XmlElement   docElement;
            XmlNodeList  allNodes;
            XmlNode      node, parent;
            int          insertionPoint;

            this.ReportProgress(BuildStep.CombiningIntermediateTocFiles,
                                "Combining conceptual and API intermediate TOC files...");

            if (!this.ExecutePlugIns(ExecutionBehaviors.InsteadOf))
            {
                this.ExecutePlugIns(ExecutionBehaviors.Before);

                // Load the TOC files
                if (toc != null && toc.Count != 0)
                {
                    conceptualXml = new XmlDocument();
                    conceptualXml.Load(workingFolder + "_ConceptualTOC_.xml");
                }

                tocXml = new XmlDocument();
                tocXml.Load(workingFolder + "toc.xml");

                // Merge the conceptual and API TOCs into one?
                if (conceptualXml != null)
                {
                    // Remove the root content container if present as we don't need it for the other formats
                    if ((project.HelpFileFormat & HelpFileFormats.MSHelpViewer) != 0 &&
                        !String.IsNullOrEmpty(this.RootContentContainerId))
                    {
                        docElement = conceptualXml.DocumentElement;
                        node       = docElement.FirstChild;
                        allNodes   = node.SelectNodes("topic");

                        foreach (XmlNode n in allNodes)
                        {
                            n.ParentNode.RemoveChild(n);
                            docElement.AppendChild(n);
                        }

                        node.ParentNode.RemoveChild(node);
                    }

                    if (String.IsNullOrEmpty(this.ApiTocParentId))
                    {
                        // If not parented, the API content is placed above or below the conceptual content based
                        // on the project's ContentPlacement setting.
                        if (project.ContentPlacement == ContentPlacement.AboveNamespaces)
                        {
                            docElement = conceptualXml.DocumentElement;

                            foreach (XmlNode n in tocXml.SelectNodes("topics/topic"))
                            {
                                node = conceptualXml.ImportNode(n, true);
                                docElement.AppendChild(node);
                            }

                            tocXml = conceptualXml;
                        }
                        else
                        {
                            docElement = tocXml.DocumentElement;

                            foreach (XmlNode n in conceptualXml.SelectNodes("topics/topic"))
                            {
                                node = tocXml.ImportNode(n, true);
                                docElement.AppendChild(node);
                            }
                        }
                    }
                    else
                    {
                        // Parent the API content to a conceptual topic
                        parent = conceptualXml.SelectSingleNode("//topic[@id='" + this.ApiTocParentId + "']");

                        // If not found, parent it to the root
                        if (parent == null)
                        {
                            parent = conceptualXml.DocumentElement;
                        }

                        insertionPoint = this.ApiTocOrder;

                        if (insertionPoint == -1 || insertionPoint >= parent.ChildNodes.Count)
                        {
                            insertionPoint = parent.ChildNodes.Count;
                        }

                        foreach (XmlNode n in tocXml.SelectNodes("topics/topic"))
                        {
                            node = conceptualXml.ImportNode(n, true);

                            if (insertionPoint >= parent.ChildNodes.Count)
                            {
                                parent.AppendChild(node);
                            }
                            else
                            {
                                parent.InsertBefore(node, parent.ChildNodes[insertionPoint]);
                            }

                            insertionPoint++;
                        }

                        tocXml = conceptualXml;
                    }

                    // Fix up empty container nodes by removing the file attribute and setting the ID attribute to
                    // the title attribute value.
                    foreach (XmlNode n in tocXml.SelectNodes("//topic[@title]"))
                    {
                        attr = n.Attributes["file"];

                        if (attr != null)
                        {
                            n.Attributes.Remove(attr);
                        }

                        attr = n.Attributes["id"];

                        if (attr != null)
                        {
                            attr.Value = n.Attributes["title"].Value;
                        }
                    }

                    tocXml.Save(workingFolder + "toc.xml");
                }

                this.ExecutePlugIns(ExecutionBehaviors.After);
            }

            // Determine the default topic for Help 1, website, and markdown output if one was not specified in a
            // site map or content layout file.
            if (defaultTopic == null && (project.HelpFileFormat & (HelpFileFormats.HtmlHelp1 |
                                                                   HelpFileFormats.Website | HelpFileFormats.Markdown)) != 0)
            {
                var defTopic = ComponentUtilities.XmlStreamAxis(workingFolder + "toc.xml", "topic").FirstOrDefault(
                    t => t.Attribute("file") != null);

                if (defTopic != null)
                {
                    // Find the file.  Could be .htm, .html, or .md so just look for any file with the given name.
                    defaultTopic = Directory.EnumerateFiles(workingFolder + "Output",
                                                            defTopic.Attribute("file").Value + ".*", SearchOption.AllDirectories).FirstOrDefault();

                    if (defaultTopic != null)
                    {
                        defaultTopic = defaultTopic.Substring(workingFolder.Length + 7);

                        if (defaultTopic.IndexOf('\\') != -1)
                        {
                            defaultTopic = defaultTopic.Substring(defaultTopic.IndexOf('\\') + 1);
                        }
                    }
                }

                // This shouldn't happen anymore, but just in case...
                if (defaultTopic == null)
                {
                    throw new BuilderException("BE0026", "Unable to determine default topic in toc.xml.  Mark " +
                                               "one as the default topic manually in the content layout file.");
                }
            }
        }
예제 #8
0
        //=====================================================================

        /// <summary>
        /// This is called to generate the namespace summary file
        /// </summary>
        private void GenerateNamespaceSummaries()
        {
            XmlNode member;
            NamespaceSummaryItem nsi;
            string nsName = null, summaryText;
            bool   isDocumented;

            this.ReportProgress(BuildStep.GenerateNamespaceSummaries, "Generating namespace summary information...");

            // Add a dummy file if there are no comments files specified.  This will contain the project and
            // namespace summaries.
            if (commentsFiles.Count == 0)
            {
                nsName = workingFolder + "_ProjNS_.xml";

                using (StreamWriter sw = new StreamWriter(nsName, false, Encoding.UTF8))
                {
                    sw.Write("<?xml version=\"1.0\"?>\r\n<doc>\r\n" +
                             "<assembly>\r\n<name>_ProjNS_</name>\r\n" +
                             "</assembly>\r\n<members/>\r\n</doc>\r\n");
                }

                commentsFiles.Add(new XmlCommentsFile(nsName));
            }

            // Replace any "NamespaceDoc" and "NamespaceGroupDoc" class IDs with their containing namespace.
            // The comments in these then become the comments for the namespaces and namespace groups.
            commentsFiles.ReplaceNamespaceDocEntries();

            if (this.ExecutePlugIns(ExecutionBehaviors.InsteadOf))
            {
                return;
            }

            this.ExecutePlugIns(ExecutionBehaviors.Before);

            // XML comments do not support summaries on namespace elements by default.  However, if Sandcastle
            // finds them, it will add them to the help file.  The same holds true for project comments on the
            // root namespaces page (R:Project_[HtmlHelpName]).  We can accomplish this by adding elements to
            // the first comments file or by supplying them in an external XML comments file.
            try
            {
                // Add the project comments if specified
                if (project.ProjectSummary.Length != 0)
                {
                    // Set the name in case it isn't valid
                    nsName = "R:Project_" + this.ResolvedHtmlHelpName.Replace(" ", "_");
                    member = commentsFiles.FindMember(nsName);
                    this.AddNamespaceComments(member, project.ProjectSummary);
                }

                // Get all the namespace and namespace group nodes from the reflection information file
                var nsElements =
                    ComponentUtilities.XmlStreamAxis(reflectionFile, "api").Where(el =>
                {
                    string id = el.Attribute("id").Value;

                    return(id.Length > 1 && id[1] == ':' && (id[0] == 'N' || id[0] == 'G') &&
                           el.Element("topicdata").Attribute("group").Value != "rootGroup");
                }).Select(el => el.Attribute("id").Value);

                // Add the namespace summaries
                foreach (var n in nsElements)
                {
                    nsName = n;

                    nsi = project.NamespaceSummaries[nsName.StartsWith("N:", StringComparison.Ordinal) ?
                                                     nsName.Substring(2) : nsName.Substring(2) + " (Group)"];

                    if (nsi != null)
                    {
                        isDocumented = nsi.IsDocumented;
                        summaryText  = nsi.Summary;
                    }
                    else
                    {
                        // The global namespace is not documented by default
                        isDocumented = (nsName != "N:");
                        summaryText  = String.Empty;
                    }

                    if (isDocumented)
                    {
                        // If documented, add the summary text
                        member = commentsFiles.FindMember(nsName);
                        this.AddNamespaceComments(member, summaryText);
                    }
                }
            }
            catch (Exception ex)
            {
                // Eat the error in a partial build so that the user can get into the namespace comments editor
                // to fix it.
                if (this.PartialBuildType != PartialBuildType.None)
                {
                    throw new BuilderException("BE0012", String.Format(CultureInfo.CurrentCulture,
                                                                       "Error generating namespace summaries (Namespace = {0}): {1}", nsName, ex.Message), ex);
                }
            }

            this.ExecutePlugIns(ExecutionBehaviors.After);
        }
예제 #9
0
        /// <summary>
        /// This is used to get an enumerable list of unique namespaces referenced in the XML comments files
        /// </summary>
        /// <param name="validNamespaces">An enumerable list of valid namespaces</param>
        /// <returns>An enumerable list of unique namespaces referenced in the XML comments files</returns>
        public IEnumerable <string> GetReferencedNamespaces(IEnumerable <string> validNamespaces)
        {
            HashSet <string> seenNamespaces = new HashSet <string>();
            string           ns;

            foreach (XmlCommentsFile f in this)
            {
                // Find all comments elements with a reference.  XML comments files may be ill-formed so
                // ignore any elements without a cref attribute.
                IEnumerable <string> crefs;

                try
                {
                    crefs = ComponentUtilities.XmlStreamAxis(f.SourcePath, new[] { "event", "exception",
                                                                                   "inheritdoc", "permission", "see", "seealso" }).Select(
                        el => (string)el.Attribute("cref")).Where(c => c != null).ToList();
                }
                catch (XmlException)
                {
                    yield break;
                }

                foreach (string refId in crefs)
                {
                    if (refId.Length > 2 && refId[1] == ':' && refId.IndexOfAny(new[] { '.', '(' }) != -1)
                    {
                        ns = refId.Trim();

                        // Strip off member name?
                        if (!ns.StartsWith("R:", StringComparison.OrdinalIgnoreCase) &&
                            !ns.StartsWith("G:", StringComparison.OrdinalIgnoreCase) &&
                            !ns.StartsWith("N:", StringComparison.OrdinalIgnoreCase) &&
                            !ns.StartsWith("T:", StringComparison.OrdinalIgnoreCase))
                        {
                            if (ns.IndexOf('(') != -1)
                            {
                                ns = ns.Substring(0, ns.IndexOf('('));
                            }

                            if (ns.IndexOf('.') != -1)
                            {
                                ns = ns.Substring(0, ns.LastIndexOf('.'));
                            }
                        }

                        if (ns.IndexOf('.') != -1)
                        {
                            ns = ns.Substring(2, ns.LastIndexOf('.') - 2);
                        }
                        else
                        {
                            ns = ns.Substring(2);
                        }

                        if (validNamespaces.Contains(ns) && !seenNamespaces.Contains(ns))
                        {
                            seenNamespaces.Add(ns);
                            yield return(ns);
                        }
                    }
                }
            }
        }