// 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 components = new List(); 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 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 { public TopicManifest (string manifest) { this.manifest = manifest; } private string manifest; public IEnumerator GetEnumerator () { return (new TopicEnumerator(manifest)); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () { return (GetEnumerator()); } } private class TopicEnumerator : IEnumerator { 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 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 components = new List(); 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); } } } }