summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-09-20 21:18:59 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-09-21 08:06:22 -0700
commitbbe3f9cc9c8a1e5909273c1a162a63ea7a66afd8 (patch)
treec91f66e642c4d26fca266e226b3f2765f546d700 /tools/Sandcastle/Source/BuildAssembler/BuildComponents/LiveExampleComponent.cs
parent627014f0bbc3fd576277375e70f8391d150b0a67 (diff)
downloadDotNetOpenAuth-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.cs193
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