summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildComponents/TransformComponent.cs
blob: 82b3241752c6ee280073347b9b97ca1f0f09aab3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace Microsoft.Ddue.Tools {

	public class TransformComponent : BuildComponent {
		
		public TransformComponent (BuildAssembler assembler, XPathNavigator configuration) : base(assembler, configuration) {

			// load the transforms
			XPathNodeIterator transform_nodes = configuration.Select("transform");
			foreach (XPathNavigator transform_node in transform_nodes) {

				// load the transform
				string file = transform_node.GetAttribute("file", String.Empty);
				if (String.IsNullOrEmpty(file)) WriteMessage(MessageLevel.Error, "Each transform element must specify a file attribute.");
				file = Environment.ExpandEnvironmentVariables(file);

				Transform transform = null;
				try {
					transform = new Transform(file);
				} catch (IOException e) {
					WriteMessage(MessageLevel.Error, String.Format("The transform file '{0}' could not be loaded. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e)));
				} catch (XmlException e) {
					WriteMessage(MessageLevel.Error, String.Format("The transform file '{0}' is not a valid XML file. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e)));
				} catch (XsltException e) {
					WriteMessage(MessageLevel.Error, String.Format("The XSL transform '{0}' contains an error. The error message is: {1}", file, BuildComponentUtilities.GetExceptionMessage(e))); 
				}


				transforms.Add(transform);


				// load any arguments
				XPathNodeIterator argument_nodes = transform_node.Select("argument");
				foreach (XPathNavigator argument_node in argument_nodes) {
					string key = argument_node.GetAttribute("key", String.Empty);
					if ((key == null) || (key.Length == 0)) WriteMessage(MessageLevel.Error, "When creating a transform argument, you must specify a key using the key attribute");

                    // set "expand-value" attribute to true to expand environment variables embedded in "value".
                    string expand_attr = argument_node.GetAttribute("expand-value", String.Empty);
                    bool expand_value = String.IsNullOrEmpty(expand_attr) ? false : Convert.ToBoolean(expand_attr);

					string value = argument_node.GetAttribute("value", String.Empty);
					if ((value != null) && (value.Length > 0)) {
                        transform.Arguments.AddParam(key, String.Empty, expand_value ? Environment.ExpandEnvironmentVariables(value) : value);
                    }
                    else {
						transform.Arguments.AddParam(key, String.Empty, argument_node.Clone());
					}
				}			

			}

		}

		// the stored transforms

		private List<Transform> transforms = new List<Transform>();

		// the action of the component

		public override void Apply (XmlDocument document, string key) {

			// iterate over transforms
			foreach (Transform transform in transforms) {

				// add the key as a parameter to the arguments
				transform.Arguments.RemoveParam("key", String.Empty);
				transform.Arguments.AddParam("key", String.Empty, key);

				// create a buffer into which output can be written
				using (MemoryStream buffer = new MemoryStream()) {


					// do the transform, routing output to the buffer
                    XmlWriterSettings settings = transform.Xslt.OutputSettings;
                    XmlWriter writer = XmlWriter.Create(buffer, settings);
                    try {
                        transform.Xslt.Transform(document, transform.Arguments, writer);
                    } catch (XsltException e) {
                        WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message));
                    } catch (XmlException e) {
                        WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message));
                    } finally {
						writer.Close();
					}

					// replace the document by the contents of the buffer
					buffer.Seek(0, SeekOrigin.Begin);

                    // some settings to ensure that we don't try to go get, parse, and validate using any referenced schemas or DTDs
                    XmlReaderSettings readerSettings = new XmlReaderSettings();
                    readerSettings.ProhibitDtd = false;
                    readerSettings.XmlResolver = null;

                    XmlReader reader = XmlReader.Create(buffer, readerSettings);
                    try {
                        document.Load(reader);
                    } catch (XmlException e) {
                        WriteMessage(MessageLevel.Error, String.Format("A error ocurred while executing the transform '{0}', on line {1}, at position {2}. The error message was: {3}", e.SourceUri, e.LineNumber, e.LinePosition, (e.InnerException == null) ? e.Message : e.InnerException.Message));
                    } finally {
                        reader.Close();
                    }

				}
			}
	
		}

	}


	// a represenataion of a transform action

	internal class Transform {

		public Transform (string file) {
            // The transforms presumably come from a trusted source, so there's no reason
            // not to enable scripting and the document function. The latter is used to read topic
            // info files for the conceptual WebDocs build.
			xslt.Load(file, new XsltSettings(true, true), new XmlUrlResolver());
		}

		private XslCompiledTransform xslt = new XslCompiledTransform();

		private XsltArgumentList arguments = new XsltArgumentList();

		public XslCompiledTransform Xslt {
			get {
				return(xslt);
			}
		}

		public XsltArgumentList Arguments {
			get {
				return(arguments);
			}
		}

	}

}