diff options
Diffstat (limited to 'tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs')
-rw-r--r-- | tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs | 316 |
1 files changed, 316 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..fb7fd47 --- /dev/null +++ b/tools/Sandcastle/Source/BuildAssembler/BuildAssemblerLibrary/BuildAssembler.cs @@ -0,0 +1,316 @@ +// Copyright (c) Microsoft Corporation. All 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(); + + public 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 virtual void Dispose () { + 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 virtual void Dispose () { + 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; + } + } + + private void SetupContextFromConfiguration(XPathDocument configuration) + { + // Load namespaces into context + XPathNodeIterator namespace_nodes = configuration.CreateNavigator().Select("/configuration/dduetools/builder/context/namespace"); + foreach (XPathNavigator namespace_node in namespace_nodes) + { + string prefix = namespace_node.GetAttribute("prefix", String.Empty); + string uri = namespace_node.GetAttribute("uri", String.Empty); + context.AddNamespace(prefix, uri); + } + } + + } + +} |