summaryrefslogtreecommitdiffstats
path: root/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary
diff options
context:
space:
mode:
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary')
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs320
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssemblerLibrary.csproj94
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponent.cs82
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponentUtilities.cs146
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildContext.cs176
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/GlobalSuppressions.cs45
-rw-r--r--tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/Properties/AssemblyInfo.cs43
7 files changed, 906 insertions, 0 deletions
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs
new file mode 100644
index 0000000..ead3616
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs
@@ -0,0 +1,320 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using System.Xml.XPath;
+
+using Microsoft.Ddue.Tools.CommandLine;
+
+namespace Microsoft.Ddue.Tools {
+
+ public class BuildAssembler : IDisposable {
+
+ // the built context
+ public BuildAssembler()
+ {
+ this.handler = BuildAssembler.ConsoleMessageHandler;
+
+ }
+
+ // the built context
+ public BuildAssembler(MessageHandler messageHandler)
+ {
+ if (messageHandler == null) throw new ArgumentNullException("messageHandler");
+ this.handler = messageHandler;
+
+ }
+
+ // private data
+
+ private BuildContext context = new BuildContext();
+
+ private List<BuildComponent> components = new List<BuildComponent>();
+
+ private MessageHandler handler;
+
+ // data accessors
+
+ public BuildContext Context {
+ get {
+ return (context);
+ }
+ }
+
+ public BuildComponent[] BuildComponents {
+ get {
+ return (components.ToArray());
+ }
+ }
+
+ public MessageHandler MessageHandler {
+ get {
+ return (handler);
+ }
+ }
+
+ // component communication mechanism
+
+ public event EventHandler ComponentEvent;
+
+ internal void OnComponentEvent (Object o, EventArgs e) {
+ if (ComponentEvent != null) ComponentEvent(o, e);
+ }
+
+ // operations
+
+ public int Apply(IEnumerable<string> topics)
+ {
+ int count = 0;
+
+ foreach (string topic in topics)
+ {
+
+ // create the document
+ XmlDocument document = new XmlDocument();
+ document.PreserveWhitespace = true;
+
+ // write a log message
+ WriteMessage(MessageLevel.Info, String.Format("Building topic {0}", topic));
+
+ // apply the component stack
+ foreach (BuildComponent component in components)
+ {
+
+ component.Apply(document, topic);
+ }
+
+ count++;
+ }
+
+ return (count);
+ }
+
+ public int Apply (string manifestFile) {
+ return (Apply(new TopicManifest(manifestFile)));
+ }
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ foreach (BuildComponent component in components) {
+ ((IDisposable) component).Dispose();
+ }
+ }
+ }
+
+ private class TopicManifest : IEnumerable<string> {
+
+ public TopicManifest (string manifest) {
+ this.manifest = manifest;
+ }
+
+ private string manifest;
+
+ public IEnumerator<string> GetEnumerator () {
+ return (new TopicEnumerator(manifest));
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
+ return (GetEnumerator());
+ }
+
+ }
+
+ private class TopicEnumerator : IEnumerator<string> {
+
+ public TopicEnumerator (string manifest) {
+ reader = XmlReader.Create(manifest);
+ reader.MoveToContent();
+ }
+
+ private XmlReader reader;
+
+ public bool MoveNext () {
+ while (reader.Read()) {
+ if ((reader.NodeType == XmlNodeType.Element) && (reader.LocalName == "topic")) return (true);
+ }
+ return (false);
+ }
+
+ public string Current {
+ get {
+ string id = reader.GetAttribute("id");
+ return(id);
+ }
+ }
+
+ Object System.Collections.IEnumerator.Current {
+ get {
+ return (Current);
+ }
+ }
+
+ public void Reset () {
+ throw new InvalidOperationException();
+ }
+
+ public void Dispose () {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ reader.Close();
+ }
+ }
+
+ }
+
+ public IEnumerable<BuildContext> GetFileManifestBuildContextEnumerator(string manifestFilename)
+ {
+ using (XmlReader reader = XmlReader.Create(manifestFilename))
+ {
+ reader.MoveToContent();
+ while (reader.Read())
+ {
+ if ((reader.NodeType == XmlNodeType.Element) && (reader.LocalName == "topic"))
+ {
+ BuildContext thisContext = new BuildContext();
+
+ try
+ {
+ string id = reader.GetAttribute("id");
+
+ while (reader.MoveToNextAttribute())
+ {
+ string name = reader.Name;
+ string value = reader.Value;
+ thisContext.AddVariable(name, value);
+ }
+ }
+ catch (XmlException e)
+ {
+ throw new XmlException(String.Format("The manifest file: '{0}' is not well-formed. The error message is: {1}", manifestFilename, e.Message), e);
+ }
+
+ yield return thisContext;
+ }
+ }
+ }
+ }
+
+ public BuildComponent LoadComponent (XPathNavigator configuration) {
+
+ if (configuration == null) throw new ArgumentNullException("configuration");
+
+ // get the component infomation
+ string assemblyName = configuration.GetAttribute("assembly", String.Empty);
+ if (String.IsNullOrEmpty(assemblyName)) {
+ WriteMessage(MessageLevel.Error, "Each component element must have an assembly attribute that specifys a path to the component assembly.");
+ }
+
+ string typeName = configuration.GetAttribute("type", String.Empty);
+ if (String.IsNullOrEmpty(typeName)) {
+ WriteMessage(MessageLevel.Error, "Each component element must have a type attribute that specifys the fully qualified name of a component type.");
+ }
+
+ // expand environmet variables in path of assembly name
+ assemblyName = Environment.ExpandEnvironmentVariables(assemblyName);
+
+ // load and instantiate the component
+ BuildComponent component = null;
+ try {
+
+ Assembly assembly = Assembly.LoadFrom(assemblyName);
+ component = (BuildComponent) assembly.CreateInstance(typeName, false, BindingFlags.Public | BindingFlags.Instance, null, new Object[2] { this, configuration }, null, null);
+
+ } catch (IOException e) {
+ WriteMessage(MessageLevel.Error, String.Format("A file access error occured while attempting to load the build component assembly '{0}'. The error message is: {1}", assemblyName, e.Message));
+ } catch (BadImageFormatException e) {
+ WriteMessage(MessageLevel.Error, String.Format("The build component assembly '{0}' is not a valid managed assembly. The error message is: {1}", assemblyName, e.Message));
+ } catch (TypeLoadException) {
+ WriteMessage(MessageLevel.Error, String.Format("The build component '{0}' was not found in the assembly '{1}'.", typeName, assemblyName));
+ } catch (MissingMethodException e) {
+ WriteMessage(MessageLevel.Error, String.Format("No appropriate constructor exists for the build component '{0}' in the component assembly '{1}'. The error message is: {1}", typeName, assemblyName, e.Message));
+ } catch (TargetInvocationException e) {
+ WriteMessage(MessageLevel.Error, String.Format("An error occured while initializing the build component '{0}' in the component assembly '{1}'. The error message and stack trace follows: {2}", typeName, assemblyName, e.InnerException.ToString()));
+ } catch (InvalidCastException) {
+ WriteMessage(MessageLevel.Error, String.Format("The type '{0}' in the component assembly '{1}' is not a build component.", typeName, assemblyName));
+ }
+
+ if (component == null) {
+ WriteMessage(MessageLevel.Error, String.Format("The type '{0}' was not found in the component assembly '{1}'.", typeName, assemblyName));
+ }
+
+ return (component);
+
+
+ }
+
+ public BuildComponent[] LoadComponents (XPathNavigator configuration) {
+
+ XPathNodeIterator componentNodes = configuration.Select("component");
+
+ List<BuildComponent> components = new List<BuildComponent>();
+
+ foreach (XPathNavigator componentNode in componentNodes) {
+ components.Add(LoadComponent(componentNode));
+ }
+
+ return(components.ToArray());
+
+ }
+
+ // routines to add and remove components from the
+
+ public void AddComponents (XPathNavigator configuration) {
+ BuildComponent[] componentsToAdd = LoadComponents(configuration);
+ foreach (BuildComponent componentToAdd in componentsToAdd) {
+ components.Add(componentToAdd);
+ }
+ }
+
+ public void ClearComponents () {
+ components.Clear();
+ }
+ private void WriteMessage(MessageLevel level, string message)
+ {
+ handler(this.GetType(), level, message);
+ }
+
+ // the default message handler
+
+ public static MessageHandler ConsoleMessageHandler {
+ get {
+ return (new MessageHandler(WriteComponentMessageToConsole));
+ }
+ }
+
+ private static void WriteComponentMessageToConsole(Type type, MessageLevel level, string message)
+ {
+ string text = String.Format("{0}: {1}", type.Name, message);
+ switch (level)
+ {
+ case MessageLevel.Info:
+ ConsoleApplication.WriteMessage(LogLevel.Info, text);
+ break;
+ case MessageLevel.Warn:
+ ConsoleApplication.WriteMessage(LogLevel.Warn, text);
+ break;
+ case MessageLevel.Error:
+ ConsoleApplication.WriteMessage(LogLevel.Error, text);
+ Environment.Exit(1);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssemblerLibrary.csproj b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssemblerLibrary.csproj
new file mode 100644
index 0000000..c7acabd
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssemblerLibrary.csproj
@@ -0,0 +1,94 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{399E78F8-4954-409E-991A-37DA9D0579CC}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>BuildAssemblerLibrary</RootNamespace>
+ <AssemblyName>BuildAssemblerLibrary</AssemblyName>
+ <SccProjectName>
+ </SccProjectName>
+ <SccLocalPath>
+ </SccLocalPath>
+ <SccAuxPath>
+ </SccAuxPath>
+ <SccProvider>
+ </SccProvider>
+ <SignAssembly>false</SignAssembly>
+ <AssemblyOriginatorKeyFile>../../../key.snk</AssemblyOriginatorKeyFile>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>2.0</OldToolsVersion>
+ <UpgradeBackupLocation>
+ </UpgradeBackupLocation>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'WebDocsDebug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\WebDocsDebug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+ <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+ <ErrorReport>prompt</ErrorReport>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Sandcastle|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\Sandcastle\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
+ <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
+ <ErrorReport>prompt</ErrorReport>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.configuration" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="BuildAssembler.cs" />
+ <Compile Include="BuildComponent.cs" />
+ <Compile Include="BuildComponentUtilities.cs" />
+ <Compile Include="BuildContext.cs" />
+ <Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\CommandLine\CommandLine.csproj">
+ <Project>{6CF7CA42-3706-4F6B-A2B4-10EF3F511888}</Project>
+ <Name>CommandLine</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <!-- Copy the output assemblies to a common binaries directory (ProductionTools). -->
+ <Target Name="AfterBuild">
+ <CreateItem Include="$(OutputPath)\$(AssemblyName).*">
+ <Output TaskParameter="Include" ItemName="ProductionFiles" />
+ </CreateItem>
+ <Copy SourceFiles="@(ProductionFiles)" DestinationFolder="..\..\..\ProductionTools" />
+ </Target>
+</Project> \ No newline at end of file
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponent.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponent.cs
new file mode 100644
index 0000000..04a68ea
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponent.cs
@@ -0,0 +1,82 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using System.Xml.XPath;
+
+namespace Microsoft.Ddue.Tools {
+
+ public abstract class BuildComponent : IDisposable {
+
+ protected BuildComponent (BuildAssembler assembler, XPathNavigator configuration) {
+ this.assembler = assembler;
+ WriteMessage(MessageLevel.Info, "Instantiating component.");
+ }
+
+ public abstract void Apply (XmlDocument document, string key);
+
+ public virtual void Apply (XmlDocument document) {
+ Apply(document, null);
+ }
+
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose (bool disposing) {
+ }
+
+ // shared data
+
+ private BuildAssembler assembler;
+
+ public BuildAssembler BuildAssembler {
+ get {
+ return(assembler);
+ }
+ }
+
+ //private MessageHandler handler;
+
+ private static Dictionary<string,object> data = new Dictionary<string,object>();
+
+ protected static Dictionary<string,object> Data {
+ get {
+ return(data);
+ }
+ }
+
+ // component messaging facility
+
+ protected void OnComponentEvent (EventArgs e) {
+ assembler.OnComponentEvent(this.GetType(), e);
+ }
+
+ protected void WriteMessage (MessageLevel level, string message) {
+ if (level == MessageLevel.Ignore) return;
+ MessageHandler handler = assembler.MessageHandler;
+ if (handler != null) handler(this.GetType(), level, message);
+ }
+
+ }
+
+ public enum MessageLevel {
+ Ignore, // don't show at all
+ Info, // informational message
+ Warn, // a minor problem occured
+ Error // a major problem occured
+ }
+
+
+ public delegate void MessageHandler (Type component, MessageLevel level, string message);
+
+
+}
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponentUtilities.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponentUtilities.cs
new file mode 100644
index 0000000..0ab709a
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildComponentUtilities.cs
@@ -0,0 +1,146 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+using System;
+using System.Text;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+namespace Microsoft.Ddue.Tools {
+
+ public static class BuildComponentUtilities {
+
+ // get the message strings from an exception
+
+ public static string GetExceptionMessage (Exception e) {
+ if (e == null) throw new ArgumentNullException("e");
+
+ string message = e.Message;
+
+ XmlException xmlE = e as XmlException;
+ if (xmlE != null) {
+ message = String.Format("{0} (LineNumber: {1}; LinePosition: {2}; SourceUri: '{3}')", message, xmlE.LineNumber, xmlE.LinePosition, xmlE.SourceUri);
+ }
+
+ XsltException xslE = e as XsltException;
+ if (xslE != null) {
+ message = String.Format("{0} (LineNumber: {1}; LinePosition: {2}; SourceUri: '{3}')", message, xslE.LineNumber, xslE.LinePosition, xslE.SourceUri);
+ }
+
+ if (e.InnerException != null) message = String.Format("{0} {1}", message, GetExceptionMessage(e.InnerException));
+
+ return (message);
+ }
+
+ // get InnerXml without changing the spacing
+
+ public static string GetInnerXml (XPathNavigator node) {
+
+ // check for null argument, and clone so we don't change input
+ if (node == null) throw new ArgumentNullException("node");
+ XPathNavigator current = node.Clone();
+
+ // create appropriate settings for the output writer
+ XmlWriterSettings settings = new XmlWriterSettings();
+ settings.ConformanceLevel = ConformanceLevel.Fragment;
+ settings.OmitXmlDeclaration = true;
+
+ // construct a writer for our output
+ StringBuilder builder = new StringBuilder();
+ XmlWriter writer = XmlWriter.Create(builder, settings);
+
+ // write the output
+ bool writing = current.MoveToFirstChild();
+ while (writing) {
+ current.WriteSubtree(writer);
+ writing = current.MoveToNext();
+ }
+
+ // finish up and return the result
+ writer.Close();
+ return(builder.ToString());
+
+ }
+
+ // get an array of nodes matching an XPath expression
+
+ public static XPathNavigator[] ConvertNodeIteratorToArray (XPathNodeIterator iterator) {
+ XPathNavigator[] result = new XPathNavigator[iterator.Count];
+ for (int i = 0; i < result.Length; i++) {
+ iterator.MoveNext();
+ result[i] = iterator.Current.Clone();
+ // clone is required or all entries will equal Current!
+ }
+ return(result);
+ }
+
+
+ /// <summary>
+ /// Returns the string result from evaluating an xpath expression against the given document and context.
+ /// </summary>
+ public static string EvalXPathExpr(IXPathNavigable doc, XPathExpression xpe, CustomContext c) {
+ XPathExpression t = xpe.Clone();
+ t.SetContext(c);
+ return doc.CreateNavigator().Evaluate(t).ToString();
+ }
+
+
+ /// <summary>
+ /// Returns the string result from evaluating an xpath expression against the given document and
+ /// context created from key/value pairs.
+ /// </summary>
+ /// <example>
+ /// string result = BuildComponentUtilities.EvalXPathExpr(doc, "concat($key, '.htm')", "key", "file");
+ /// </example>
+ public static string EvalXPathExpr(IXPathNavigable doc, XPathExpression xpe, params string[] keyValuePairs) {
+ Debug.Assert(keyValuePairs.Length % 2 == 0);
+ CustomContext cc = new CustomContext();
+ for (int i = 0; i < keyValuePairs.Length; i += 2)
+ cc[keyValuePairs[i]] = keyValuePairs[i + 1];
+ return EvalXPathExpr(doc, xpe, cc);
+ }
+
+ /// <summary>
+ /// Returns the path argument adjusted to be relative to the base path. Absolute path names will
+ /// be returned unchanged.
+ /// </summary>
+ /// <example>
+ /// path: "xxx/aaa/target.html"
+ /// basePath: "xxx/bbb/source.html"
+ /// result: "../aaa/target.html"
+ /// </example>
+ public static string GetRelativePath(string path, string basePath) {
+ // ignore absolute path names and an empty basePath
+ if (!string.IsNullOrEmpty(path) && path[0] != '/' && !string.IsNullOrEmpty(basePath)) {
+
+ List<string> pathParts = new List<string>(path.Split('/'));
+ List<string> basePathParts = new List<string>(basePath.Split('/'));
+
+ // remove the base path file name
+ if (basePathParts.Count > 0)
+ basePathParts.RemoveAt(basePathParts.Count - 1);
+
+ // strip common base path bits
+ while (pathParts.Count > 0 && basePathParts.Count > 0 &&
+ string.Equals(pathParts[0], basePathParts[0], StringComparison.CurrentCultureIgnoreCase)) {
+ pathParts.RemoveAt(0);
+ basePathParts.RemoveAt(0);
+ }
+
+ // move up one level for each remaining base path part
+ foreach (string s in basePathParts)
+ pathParts.Insert(0, "..");
+
+ path = string.Join("/", pathParts.ToArray());
+ }
+
+ return path;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildContext.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildContext.cs
new file mode 100644
index 0000000..8a21675
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildContext.cs
@@ -0,0 +1,176 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using System.Xml.XPath;
+using System.Xml.Xsl;
+
+namespace Microsoft.Ddue.Tools {
+
+ public class BuildContext {
+
+ private CustomContext context = new CustomContext();
+
+ // Namespace control
+
+ public void AddNamespace (string prefix, string uri) {
+ context.AddNamespace(prefix, uri);
+ }
+
+ public string LookupNamespace (string prefix) {
+ return( context.LookupNamespace(prefix) );
+ }
+
+ public bool RemoveNamespace (string prefix) {
+ string uri = LookupNamespace(prefix);
+ if (uri == null) {
+ return(false);
+ } else {
+ context.RemoveNamespace(prefix, uri);
+ return(true);
+ }
+ }
+
+ public void ClearNamespaces () {
+ }
+
+ // Variable control
+
+ public void AddVariable (string name, string value) {
+ context[name] = value;
+ }
+
+ public string LookupVariable (string name) {
+ return( context[name] );
+ }
+
+ public bool RemoveVariable (string name) {
+ return( context.ClearVariable(name) );
+ }
+
+ public void ClearVariables () {
+ context.ClearVariables();
+ }
+
+ public string this [string name] {
+ get {
+ return(context[name]);
+ }
+ set {
+ context[name] = value;
+ }
+ }
+
+ // Function control
+
+ // The context for use in XPath queries
+
+ public XsltContext XsltContext {
+ get {
+ return(context);
+ }
+ }
+
+ // Load data from config
+
+ public void Load (XPathNavigator configuration) {
+ XPathNodeIterator namespaceNodes = configuration.Select("namespace");
+ foreach (XPathNavigator namespaceNode in namespaceNodes) {
+ string prefixValue = namespaceNode.GetAttribute("prefix", String.Empty);
+ string uriValue = namespaceNode.GetAttribute("uri", String.Empty);
+ AddNamespace(prefixValue, uriValue);
+ }
+ }
+
+ }
+
+ public class CustomContext : XsltContext {
+
+ public CustomContext() : base() {}
+
+ // variable control
+
+ private Dictionary<string, IXsltContextVariable> variables = new Dictionary<string,IXsltContextVariable>();
+
+ public string this [string variable] {
+ get {
+ return(variables[variable].Evaluate(this).ToString());
+ }
+ set {
+ variables[variable] = new CustomVariable(value);
+ }
+ }
+
+ public bool ClearVariable (string name) {
+ return( variables.Remove(name) );
+ }
+
+ public void ClearVariables () {
+ variables.Clear();
+ }
+
+ // Implementation of XsltContext methods
+
+ public override IXsltContextVariable ResolveVariable (string prefix, string name) {
+ return( variables[name] );
+ }
+
+ public override IXsltContextFunction ResolveFunction (string prefix, string name, XPathResultType[] argumentTypes) {
+ throw new NotImplementedException();
+ }
+
+ public override int CompareDocument (string baseUri, string nextBaseUri) {
+ return(0);
+ }
+
+ public override bool Whitespace {
+ get {
+ return(true);
+ }
+ }
+
+ public override bool PreserveWhitespace (XPathNavigator node) {
+ return(true);
+ }
+
+ }
+
+
+ internal struct CustomVariable : IXsltContextVariable {
+
+ public CustomVariable (string value) {
+ this.value = value;
+ }
+
+ private string value;
+
+ public bool IsLocal {
+ get {
+ return(false);
+ }
+ }
+
+ public bool IsParam {
+ get {
+ return(false);
+ }
+ }
+
+ public XPathResultType VariableType {
+ get {
+ return(XPathResultType.String);
+ }
+ }
+
+ public Object Evaluate (XsltContext context) {
+ return(value);
+ }
+
+ }
+
+
+} \ No newline at end of file
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/GlobalSuppressions.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/GlobalSuppressions.cs
new file mode 100644
index 0000000..710997a
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/GlobalSuppressions.cs
@@ -0,0 +1,45 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click
+// "In Project Suppression File".
+// You do not need to add suppressions to this file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ddue", Scope = "namespace", Target = "Microsoft.Ddue.Tools")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#Apply(System.Collections.Generic.IEnumerable`1<System.String>)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#BuildComponents")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Filename", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#GetFileManifestBuildContextEnumerator(System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#LoadComponent(System.Xml.XPath.XPathNavigator)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#LoadComponent(System.Xml.XPath.XPathNavigator)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#LoadComponent(System.Xml.XPath.XPathNavigator)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "components", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#LoadComponents(System.Xml.XPath.XPathNavigator)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildAssembler.#WriteComponentMessageToConsole(System.Type,Microsoft.Ddue.Tools.MessageLevel,System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "configuration", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponent.#.ctor(Microsoft.Ddue.Tools.BuildAssembler,System.Xml.XPath.XPathNavigator)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponent.#Apply(System.Xml.XmlDocument)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponent.#Apply(System.Xml.XmlDocument,System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "e", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#GetExceptionMessage(System.Exception)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#GetExceptionMessage(System.Exception)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object[])", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#GetExceptionMessage(System.Exception)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "s", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#GetRelativePath(System.String,System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildContext.#AddNamespace(System.String,System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildContext.#ClearNamespaces()")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Scope = "type", Target = "Microsoft.Ddue.Tools.CustomContext")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Scope = "type", Target = "Microsoft.Ddue.Tools.CustomContext")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.Ddue.Tools.CustomContext.#CompareDocument(System.String,System.String)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.Ddue.Tools.CustomContext.#ResolveFunction(System.String,System.String,System.Xml.XPath.XPathResultType[])")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Eval", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,Microsoft.Ddue.Tools.CustomContext)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Expr", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,Microsoft.Ddue.Tools.CustomContext)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,Microsoft.Ddue.Tools.CustomContext)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xpe", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,Microsoft.Ddue.Tools.CustomContext)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Eval", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,System.String[])")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Expr", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,System.String[])")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "xpe", Scope = "member", Target = "Microsoft.Ddue.Tools.BuildComponentUtilities.#EvalXPathExpr(System.Xml.XPath.IXPathNavigable,System.Xml.XPath.XPathExpression,System.String[])")]
diff --git a/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/Properties/AssemblyInfo.cs b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e87d8d5
--- /dev/null
+++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/Properties/AssemblyInfo.cs
@@ -0,0 +1,43 @@
+// Copyright © Microsoft Corporation.
+// This source file is subject to the Microsoft Permissive License.
+// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
+// All other rights reserved.
+
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("BuildAssemblerLibrary")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("BuildAssemblerLibrary")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2006")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: CLSCompliant(true)]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8dc79b50-d46d-4e02-b2e6-43abc681e189")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("2.5.10626.00")]
+[assembly: AssemblyFileVersion("2.5.10626.00")]