diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-20 21:18:59 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-09-21 08:06:22 -0700 |
commit | bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8 (patch) | |
tree | c91f66e642c4d26fca266e226b3f2765f546d700 /tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs | |
parent | 627014f0bbc3fd576277375e70f8391d150b0a67 (diff) | |
download | DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.zip DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.tar.gz DotNetOpenAuth-bbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8.tar.bz2 |
Switched out the Sandcastle binaries for the source code.
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs')
-rw-r--r-- | tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs new file mode 100644 index 0000000..fd1b5fc --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.XPath; + + +namespace Microsoft.Ddue.Tools { + + /// <summary> + /// The LiveExampleComponent replaces ddue:CodeReference elements with links to runnable code samples, + /// if the type is "run" or "view". All other kinds of code samples are passed through to the + /// standard example component, except that the type prefix is removed. A Parsnip "approval" file can + /// be used to omit code samples that did not pass validation, or to replace broken samples with a message + /// to that effect. + /// </summary> + public class LiveExampleComponent : BuildComponent { + + readonly string wdxNamespace = "http://temp.uri/wdx"; + readonly string wdxPrefix = "wdx"; + + XPathExpression selector; + XmlNamespaceManager context; + + bool omitBadExamples; + bool runBadExamples; + + Dictionary<string, SampleInfo> sampleInfoTable; + + public LiveExampleComponent(BuildAssembler assembler, XPathNavigator configuration) + : base(assembler, configuration) { + + XPathNavigator parsnip_node = configuration.SelectSingleNode("parsnip"); + string approvedFile = null; + if (parsnip_node != null) { + approvedFile = parsnip_node.GetAttribute("approved-file", String.Empty); + + string omitBadExamplesValue = parsnip_node.GetAttribute("omit-bad-examples", String.Empty); + if (!string.IsNullOrEmpty(omitBadExamplesValue)) + omitBadExamples = Boolean.Parse(omitBadExamplesValue); + + string runBadExamplesValue = parsnip_node.GetAttribute("run-bad-examples", String.Empty); + if (!string.IsNullOrEmpty(runBadExamplesValue)) + runBadExamples = Boolean.Parse(runBadExamplesValue); + } + + if (string.IsNullOrEmpty(approvedFile)) + WriteMessage(MessageLevel.Warn, "No approved samples file specified; all available samples will be included."); + else + LoadApprovedFile(approvedFile); + + context = new CustomContext(); + context.AddNamespace("ddue", "http://ddue.schemas.microsoft.com/authoring/2003/5"); + + selector = XPathExpression.Compile("//ddue:codeReference"); + selector.SetContext(context); + } + + static XPathNavigator[] ConvertIteratorToArray(XPathNodeIterator iterator) { + XPathNavigator[] result = new XPathNavigator[iterator.Count]; + for (int i = 0; i < result.Length; i++) { + iterator.MoveNext(); + result[i] = iterator.Current.Clone(); + } + return (result); + } + + public override void Apply(XmlDocument document, string key) { + XPathNodeIterator nodesIterator = document.CreateNavigator().Select(selector); + XPathNavigator[] nodes = ConvertIteratorToArray(nodesIterator); + + foreach (XPathNavigator node in nodes) { + CodeReference cref = new CodeReference(node.Value); + + SampleInfo si = null; + if (sampleInfoTable != null && cref.ExampleName != null) + sampleInfoTable.TryGetValue(cref.ExampleName, out si); + + WriteMessage(MessageLevel.Info, string.Format("*** codeReference={0}; approved={1}; type={2}", + node.Value, (si == null) ? false : si.IsApproved("CS"), cref.Type)); + + + switch (cref.Type) { + case CodeReferenceType.Msdn: + // TODO: remove "msdn:" from code reference and let ExampleComponent handle the snippet. + // We'll either pass this through to the regular ExampleComponent or delete the node. + WriteMessage(MessageLevel.Warn, "MSDN-only links not implemented yet."); + break; + + case CodeReferenceType.Run: + case CodeReferenceType.View: + if (si != null || !omitBadExamples) { + WriteMessage(MessageLevel.Info, string.Format("+ LiveCode: Kind={0}, SampleName={1}", cref.Type.ToString(), cref.ExamplePath)); + XmlWriter writer = node.InsertAfter(); + writer.WriteStartElement(wdxPrefix, "LiveCode", wdxNamespace); + writer.WriteAttributeString("Kind", cref.Type.ToString()); + writer.WriteAttributeString("SampleName", cref.ExamplePath); + writer.WriteAttributeString("runat", "server"); + writer.WriteEndElement(); + writer.Close(); + node.DeleteSelf(); + } + break; + + case CodeReferenceType.Snippet: + // Ignore; let ExampleComponent handle the snippet. + break; + + default: + WriteMessage(MessageLevel.Warn, string.Format("Invalid code example reference ignored: '{0}'", node.Value)); + break; + } + } + } + + /// <summary> + /// LoadApprovedFile reads the Parsnip approval file into a memory structure for easy lookup. We're assuming that the + /// content of this file is well formed and valid. Semantic errors will be silently ignored. Only samples with + /// approved units will be added to the lookup table. + /// </summary> + void LoadApprovedFile(string pathName) { + WriteMessage(MessageLevel.Info, string.Format("Loading Parsnip 'Approved' file: {0}", pathName)); + sampleInfoTable = new Dictionary<string, SampleInfo>(); + using (XmlReader reader = XmlReader.Create(pathName)) { + SampleInfo si = null; + string sample_name = null; + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.Element: + if (reader.Name == "Sample") { + sample_name = reader.GetAttribute("name"); + if (!string.IsNullOrEmpty(sample_name)) + si = new SampleInfo(sample_name); + } + else if (si != null && reader.Name == "Unit") { + if (reader.GetAttribute("include") == "true") + si.AddApprovedUnit(reader.GetAttribute("name")); + } + break; + case XmlNodeType.EndElement: + if (reader.Name == "Sample") { + if (si != null) { + try { + if (si.ApprovedUnitsCount > 0) + sampleInfoTable.Add(si.Name, si); + } + catch (Exception x) { + WriteMessage(MessageLevel.Warn, string.Format("Sample {0} cannot be loaded {1}", si.Name, x.Message)); + } + } + si = null; + } + break; + default: + break; + } + } + } + } + } + + internal class SampleInfo { + string _name; + internal string Name { + get { return _name; } + } + + List<string> _approvedUnits; + + internal SampleInfo(string name) { + _name = name; + } + + internal void AddApprovedUnit(string unit_name) { + if (_approvedUnits == null) + _approvedUnits = new List<string>(); + _approvedUnits.Add(unit_name); + } + + internal bool IsApproved(string unit_name) { + return (_approvedUnits == null) ? false : _approvedUnits.Contains(unit_name); + } + + internal int ApprovedUnitsCount { + get { return (_approvedUnits == null) ? 0 : _approvedUnits.Count; } + } + } +}
\ No newline at end of file |