summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs')
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs146
1 files changed, 146 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs
new file mode 100644
index 0000000..bacb26c
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildComponents/SaveComponent.cs
@@ -0,0 +1,146 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+using System;
+using System.Configuration;
+using System.Text;
+using System.Xml;
+using System.Xml.XPath;
+
+using System.IO;
+
+namespace Microsoft.Ddue.Tools {
+
+ public class SaveComponent : BuildComponent {
+
+ private CustomContext context = new CustomContext();
+
+ private XPathExpression path_expression;
+
+ private XPathExpression select_expression;
+
+ private XmlWriterSettings settings = new XmlWriterSettings();
+
+ public SaveComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) {
+
+ // load the target path format
+ XPathNavigator save_node = configuration.SelectSingleNode("save");
+ if (save_node == null) throw new ConfigurationErrorsException("When instantiating a save component, you must specify a the target file using the <save> element.");
+
+ string base_value = save_node.GetAttribute("base", String.Empty);
+ if (!String.IsNullOrEmpty(base_value)) {
+ basePath = Path.GetFullPath(Environment.ExpandEnvironmentVariables(base_value));
+ }
+
+ string path_value = save_node.GetAttribute("path", String.Empty);
+ if (String.IsNullOrEmpty(path_value)) WriteMessage(MessageLevel.Error, "Each save element must have a path attribute specifying an XPath that evaluates to the location to save the file.");
+ path_expression = XPathExpression.Compile(path_value);
+
+ string select_value = save_node.GetAttribute("select", String.Empty);
+ if (!String.IsNullOrEmpty(select_value))
+ select_expression = XPathExpression.Compile(select_value);
+
+ settings.Encoding = Encoding.UTF8;
+
+ string indent_value = save_node.GetAttribute("indent", String.Empty);
+ if (!String.IsNullOrEmpty(indent_value)) settings.Indent = Convert.ToBoolean(indent_value);
+
+ string omit_value = save_node.GetAttribute("omit-xml-declaration", String.Empty);
+ if (!String.IsNullOrEmpty(omit_value)) settings.OmitXmlDeclaration = Convert.ToBoolean(omit_value);
+
+ linkPath = save_node.GetAttribute("link", String.Empty);
+ if (String.IsNullOrEmpty(linkPath)) linkPath = "../html";
+
+ // encoding
+
+ settings.CloseOutput = true;
+
+ }
+
+ private string basePath = null;
+
+ private string linkPath = null;
+
+ public override void Apply (XmlDocument document, string key) {
+
+ // set the evaluation context
+ context["key"] = key;
+
+ XPathExpression path_xpath = path_expression.Clone();
+ path_xpath.SetContext(context);
+
+ // evaluate the path
+ string path = document.CreateNavigator().Evaluate(path_xpath).ToString();
+ string file = Path.GetFileName(path);
+
+ string fileLinkPath = Path.Combine(linkPath, file);
+ if (basePath != null) path = Path.Combine(basePath, path);
+
+
+ // *SECURIY* The path name may be derived from user entered meta data in Doc Studio and as such
+ // it is not trustworthy. To pass, the path must be inside the directory tree base_directory
+ // (which is the current directory if a path is not specified).
+
+ // This test is causing problems
+ /*
+ string absoluteBasePath = (basePath == null) ? Directory.GetCurrentDirectory() : Path.GetFullPath(basePath);
+ string targetPath = Path.GetDirectoryName(path);
+ if (!targetPath.StartsWith(absoluteBasePath, StringComparison.CurrentCultureIgnoreCase)) {
+ WriteMessage(MessageLevel.Error, string.Format("Cannot save document outside of base directory: {0}", targetPath));
+ return;
+ }
+ */
+ string targetDirectory = Path.GetDirectoryName(path);
+ if (!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory);
+
+ // save the document
+ // select_expression determines which nodes get saved. If there is no select_expression
+ // we simply save the root node as before. If there is a select_expression, we evaluate the
+ // xpath expression and save the resulting node set. The select expression also enables the
+ // "literal-text" processing instruction, which outputs its content as unescaped text.
+ if (select_expression == null) {
+ XmlNode doctype = document.DocumentType;
+ try {
+ //Console.WriteLine("path = '{0}'", path);
+ //document.Save(path);
+
+ using (XmlWriter writer = XmlWriter.Create(path, settings)) {
+ document.Save(writer);
+ }
+
+ } catch (IOException e) {
+ WriteMessage(MessageLevel.Error, String.Format("An access error occured while attempting to save to the file '{0}'. The error message is: {1}", path, BuildComponentUtilities.GetExceptionMessage(e)));
+ } catch (XmlException e) {
+ WriteMessage(MessageLevel.Error, String.Format("Invalid XML was written to the output file '{0}'. The error message is: '{1}'", path, BuildComponentUtilities.GetExceptionMessage(e)));
+ }
+
+ // Get the relative html path for HXF generation.
+ int index = fileLinkPath.IndexOf('/');
+ string htmlPath = fileLinkPath.Substring(index + 1, fileLinkPath.Length - (index + 1));
+
+ FileCreatedEventArgs fe = new FileCreatedEventArgs(htmlPath, Path.GetDirectoryName(targetDirectory));
+ OnComponentEvent(fe);
+ }
+ else {
+ // IMPLEMENTATION NOTE: The separate StreamWriter is used to maintain XML indenting.
+ // Without it the XmlWriter won't honor our indent settings after plain text nodes have been
+ // written.
+ settings.ConformanceLevel = ConformanceLevel.Auto;
+ using (StreamWriter output = File.CreateText(path)) {
+ using (XmlWriter writer = XmlWriter.Create(output, settings)) {
+ XPathExpression select_xpath = select_expression.Clone();
+ select_xpath.SetContext(context);
+ XPathNodeIterator ni = document.CreateNavigator().Select(select_expression);
+ while (ni.MoveNext()) {
+ if (ni.Current.NodeType == XPathNodeType.ProcessingInstruction && ni.Current.Name.Equals("literal-text")) {
+ writer.Flush();
+ output.Write(ni.Current.Value);
+ }
+ else
+ ni.Current.WriteSubtree(writer);
+ }
+ }
+ }
+ }
+ }
+ }
+}