// NAnt - A .NET build tool // Copyright (C) 2001-2003 Gerry Shaw // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // John R. Hicks (angryjohn69@nc.rr.com) // Gerry Shaw (gerry_shaw@yahoo.com) // William E. Caputo (wecaputo@thoughtworks.com | logosity@yahoo.com) // Gert Driesen (gert.driesen@ardatis.com) // // Some of this class was based on code from the Mono class library. // Copyright (C) 2002 John R. Hicks // // The events described in this file are based on the comments and // structure of Ant. // Copyright (C) Copyright (c) 2000,2002 The Apache Software Foundation. // All rights reserved. using System; using System.Collections; using System.Globalization; using System.IO; using System.Runtime.Remoting.Lifetime; using System.Text; using System.Web.Mail; using NAnt.Core.Types; using NAnt.Core.Util; namespace NAnt.Core { /// /// Defines the set of levels recognised by the NAnt logging system. /// public enum Level : int { /// /// Designates fine-grained informational events that are most useful /// to debug a build process. /// Debug = 1000, /// /// Designates events that offer a more detailed view of the build /// process. /// Verbose = 2000, /// /// Designates informational events that are useful for getting a /// high-level view of the build process. /// Info = 3000, /// /// Designates potentionally harmful events. /// Warning = 4000, /// /// Designates error events. /// Error = 5000, /// /// Can be used to suppress all messages. /// /// /// No events should be logged with this . /// None = 9999 } /// /// Class representing an event occurring during a build. /// /// /// /// An event is built by specifying either a project, a task or a target. /// /// /// A level event will only have a /// reference. /// /// /// A level event will have and /// references. /// /// /// A level event will have , /// and references. /// /// public class BuildEventArgs : EventArgs { #region Public Instance Constructors /// /// Initializes a new instance of the /// class. /// public BuildEventArgs() { _project = null; _target = null; _task = null; } /// /// Initializes a new instance of the /// class for a level event. /// /// The that emitted the event. public BuildEventArgs(Project project) { _project = project; _target = null; _task = null; } /// /// Initializes a new instance of the /// class for a level event. /// /// The that emitted the event. public BuildEventArgs(Target target) { _project = target.Project; _target = target; _task = null; } /// /// Initializes a new instance of the /// class for a level event. /// /// The that emitted the event. public BuildEventArgs(Task task) { _project = task.Project; _target = task.Parent as Target; _task = task; } #endregion Public Instance Constructors #region Public Instance Properties /// /// Gets or sets the message associated with this event. /// /// /// The message associated with this event. /// public string Message { get { return _message; } set { _message = value; } } /// /// Gets or sets the priority level associated with this event. /// /// /// The priority level associated with this event. /// public Level MessageLevel { get { return _messageLevel; } set { _messageLevel = value; } } /// /// Gets or sets the associated with this event. /// /// /// The associated with this event. /// public Exception Exception { get { return _exception; } set { _exception = value; } } /// /// Gets the that fired this event. /// /// /// The that fired this event. /// public Project Project { get { return _project; } } /// /// Gets the that fired this event. /// /// /// The that fired this event, or a null reference /// if this is a level event. /// public Target Target { get { return _target; } } /// /// Gets the that fired this event. /// /// /// The that fired this event, or /// if this is a or level /// event. /// public Task Task { get { return _task; } } #endregion Public Instance Properties #region Private Instance Fields private readonly Project _project; private readonly Target _target; private readonly Task _task; private string _message; private Level _messageLevel = Level.Verbose; private Exception _exception; #endregion Private Instance Fields } /// /// Represents the method that handles the build events. /// /// The source of the event. /// A that contains the event data. public delegate void BuildEventHandler(object sender, BuildEventArgs e); /// /// Instances of classes that implement this interface can register to be /// notified when things happen during a build. /// public interface IBuildListener { /// /// Signals that a build has started. /// /// The source of the event. /// A object that contains the event data. /// /// This event is fired before any targets have started. /// void BuildStarted(object sender, BuildEventArgs e); /// /// Signals that the last target has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// void BuildFinished(object sender, BuildEventArgs e); /// /// Signals that a target has started. /// /// The source of the event. /// A object that contains the event data. void TargetStarted(object sender, BuildEventArgs e); /// /// Signals that a target has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// void TargetFinished(object sender, BuildEventArgs e); /// /// Signals that a task has started. /// /// The source of the event. /// A object that contains the event data. void TaskStarted(object sender, BuildEventArgs e); /// /// Signals that a task has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// void TaskFinished(object sender, BuildEventArgs e); /// /// Signals that a message has been logged. /// /// The source of the event. /// A object that contains the event data. void MessageLogged(object sender, BuildEventArgs e); } /// /// Interface used by NAnt to log the build output. /// /// /// Depending on the supplied command-line arguments, NAnt will set the /// to or a /// with a file as backend store. /// public interface IBuildLogger : IBuildListener { /// /// Gets or sets the highest level of message this logger should respond /// to. /// /// The highest level of message this logger should respond to. /// /// Only messages with a message level higher than or equal to the given /// level should actually be written to the log. /// Level Threshold { get; set; } /// /// Gets or sets a value indicating whether to produce emacs (and other /// editor) friendly output. /// /// /// if output is to be unadorned so that emacs /// and other editors can parse files names, etc. /// bool EmacsMode { get; set; } /// /// Gets or sets the to which the logger is /// to send its output. /// TextWriter OutputWriter { get; set; } /// /// Flushes buffered build events or messages to the underlying storage. /// void Flush(); } [Serializable()] public class DefaultLogger : IBuildLogger { #region Public Instance Constructors /// /// Initializes a new instance of the /// class. /// public DefaultLogger() { } #endregion Public Instance Constructors #region Implementation of IBuildLogger /// /// Gets or sets the highest level of message this logger should respond /// to. /// /// /// The highest level of message this logger should respond to. /// /// /// Only messages with a message level higher than or equal to the given /// level should be written to the log. /// public virtual Level Threshold { get { return _threshold; } set { _threshold = value; } } /// /// Gets or sets a value indicating whether to produce emacs (and other /// editor) friendly output. /// /// /// if output is to be unadorned so that emacs /// and other editors can parse files names, etc. The default is /// . /// public virtual bool EmacsMode { get { return _emacsMode; } set { _emacsMode = value; } } /// /// Gets or sets the to which the logger is /// to send its output. /// /// /// The to which the logger sends its output. /// public virtual TextWriter OutputWriter { get { return _outputWriter; } set { _outputWriter = value; } } /// /// Flushes buffered build events or messages to the underlying storage. /// public virtual void Flush() { if (OutputWriter != null) { OutputWriter.Flush(); } } #endregion Implementation of IBuildLogger #region Implementation of IBuildListener /// /// Signals that a build has started. /// /// The source of the event. /// A object that contains the event data. /// /// This event is fired before any targets have started. /// public virtual void BuildStarted(object sender, BuildEventArgs e) { _buildReports.Push(new BuildReport(DateTime.Now)); } /// /// Signals that the last target has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// public virtual void BuildFinished(object sender, BuildEventArgs e) { Exception error = e.Exception; int indentationLevel = 0; if (e.Project != null) { indentationLevel = e.Project.IndentationLevel * e.Project.IndentationSize; } BuildReport report = (BuildReport) _buildReports.Pop(); if (error == null) { OutputMessage(Level.Info, string.Empty, indentationLevel); if (report.Errors == 0 && report.Warnings == 0) { OutputMessage(Level.Info, "BUILD SUCCEEDED", indentationLevel); } else { OutputMessage(Level.Info, string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("String_BuildSucceeded"), report.Errors, report.Warnings), indentationLevel); } OutputMessage(Level.Info, string.Empty, indentationLevel); } else { OutputMessage(Level.Error, string.Empty, indentationLevel); if (report.Errors == 0 && report.Warnings == 0) { OutputMessage(Level.Error, "BUILD FAILED", indentationLevel); } else { OutputMessage(Level.Info, string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("String_BuildFailed"), report.Errors, report.Warnings), indentationLevel); } OutputMessage(Level.Error, string.Empty, indentationLevel); if (error is BuildException) { if (Threshold <= Level.Verbose) { OutputMessage(Level.Error, error.ToString(), indentationLevel); } else { if (error.Message != null) { OutputMessage(Level.Error, error.Message, indentationLevel); } // output nested exceptions Exception nestedException = error.InnerException; int exceptionIndentationLevel = indentationLevel; int indentShift = 4; //e.Project.IndentationSize; while (nestedException != null && !StringUtils.IsNullOrEmpty(nestedException.Message)) { exceptionIndentationLevel += indentShift; OutputMessage(Level.Error, nestedException.Message, exceptionIndentationLevel); nestedException = nestedException.InnerException; } } } else { OutputMessage(Level.Error, "INTERNAL ERROR", indentationLevel); OutputMessage(Level.Error, string.Empty, indentationLevel); OutputMessage(Level.Error, error.ToString(), indentationLevel); OutputMessage(Level.Error, string.Empty, indentationLevel); OutputMessage(Level.Error, "Please send bug report to nant-developers@lists.sourceforge.net.", indentationLevel); } OutputMessage(Level.Error, string.Empty, indentationLevel); } // output total build time TimeSpan buildTime = DateTime.Now - report.StartTime; OutputMessage(Level.Info, string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("String_TotalTime") + Environment.NewLine, Math.Round(buildTime.TotalSeconds, 1)), indentationLevel); // make sure all messages are written to the underlying storage Flush(); } /// /// Signals that a target has started. /// /// The source of the event. /// A object that contains the event data. public virtual void TargetStarted(object sender, BuildEventArgs e) { int indentationLevel = 0; if (e.Project != null) { indentationLevel = e.Project.IndentationLevel * e.Project.IndentationSize; } if (e.Target != null) { OutputMessage(Level.Info, string.Empty, indentationLevel); OutputMessage( Level.Info, string.Format(CultureInfo.InvariantCulture, "{0}:", e.Target.Name), indentationLevel); OutputMessage(Level.Info, string.Empty, indentationLevel); } } /// /// Signals that a task has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// public virtual void TargetFinished(object sender, BuildEventArgs e) { } /// /// Signals that a task has started. /// /// The source of the event. /// A object that contains the event data. public virtual void TaskStarted(object sender, BuildEventArgs e) { } /// /// Signals that a task has finished. /// /// The source of the event. /// A object that contains the event data. /// /// This event will still be fired if an error occurred during the build. /// public virtual void TaskFinished(object sender, BuildEventArgs e) { } /// /// Signals that a message has been logged. /// /// The source of the event. /// A object that contains the event data. /// /// Only messages with a priority higher or equal to the threshold of /// the logger will actually be output in the build log. /// public virtual void MessageLogged(object sender, BuildEventArgs e) { if (_buildReports.Count > 0) { if (e.MessageLevel == Level.Error) { BuildReport report = (BuildReport) _buildReports.Peek(); report.Errors++; } else if (e.MessageLevel == Level.Warning) { BuildReport report = (BuildReport) _buildReports.Peek(); report.Warnings++; } } // output the message OutputMessage(e); } #endregion Implementation of IBuildListener #region Protected Instance Methods /// /// Empty implementation which allows derived classes to receive the /// output that is generated in this logger. /// /// The message being logged. protected virtual void Log(string message) { } #endregion Protected Instance Methods #region Private Instance Methods /// /// Outputs an indented message to the build log if its priority is /// greather than or equal to the of the /// logger. /// /// The priority of the message to output. /// The message to output. /// The number of characters that the message should be indented. private void OutputMessage(Level messageLevel, string message, int indentationLength) { OutputMessage(CreateBuildEvent(messageLevel, message), indentationLength); } /// /// Outputs an indented message to the build log if its priority is /// greather than or equal to the of the /// logger. /// /// The event to output. private void OutputMessage(BuildEventArgs e) { int indentationLength = 0; if (e.Project != null) { indentationLength = e.Project.IndentationLevel * e.Project.IndentationSize; } OutputMessage(e, indentationLength); } /// /// Outputs an indented message to the build log if its priority is /// greather than or equal to the of the /// logger. /// /// The event to output. /// TODO private void OutputMessage(BuildEventArgs e, int indentationLength) { if (e.MessageLevel >= Threshold) { string txt = e.Message; // beautify the message a bit txt = txt.Replace("\t", " "); // replace tabs with spaces txt = txt.Replace("\r", ""); // get rid of carriage returns // split the message by lines - the separator is "\n" since we've eliminated // \r characters string[] lines = txt.Split('\n'); string label = String.Empty; if (e.Task != null && !EmacsMode) { label = "[" + e.Task.Name + "] "; label = label.PadLeft(e.Project.IndentationSize); } if (indentationLength > 0) { label = new String(' ', indentationLength) + label; } foreach (string line in lines) { StringBuilder sb = new StringBuilder(); sb.Append(label); sb.Append(line); string indentedMessage = sb.ToString(); // output the message to the console Console.Out.WriteLine(indentedMessage); // if an OutputWriter was set, write the message to it if (OutputWriter != null) { OutputWriter.WriteLine(indentedMessage); } Log(indentedMessage); } } } #endregion Private Instance Methods #region Private Static Methods private static BuildEventArgs CreateBuildEvent(Level messageLevel, string message) { BuildEventArgs buildEvent = new BuildEventArgs(); buildEvent.MessageLevel = messageLevel; buildEvent.Message = message; return buildEvent; } #endregion Private Static Methods #region Private Instance Fields private Level _threshold = Level.Info; private TextWriter _outputWriter; private bool _emacsMode; #endregion Private Instance Fields #region Private Static Fields /// /// Holds a stack of reports for all running builds. /// private Stack _buildReports = new Stack(); #endregion Private Static Fields } /// /// Used to store information about a build, to allow better reporting to /// the user. /// [Serializable()] public class BuildReport { /// /// Errors encountered so far. /// public int Errors; /// /// Warnings encountered so far. /// public int Warnings; /// /// The start time of the build process. /// public DateTime StartTime; public BuildReport(DateTime startTime) { StartTime = startTime; Errors = 0; Warnings = 0; } } /// /// Buffers log messages from DefaultLogger, and sends an e-mail with the /// results. /// /// /// The following properties are used to send the mail : /// /// /// Property /// Description /// /// /// MailLogger.mailhost /// Mail server to use. [default: localhost] /// /// /// MailLogger.from /// The address of the e-mail sender. /// /// /// MailLogger.failure.notify /// Send build failure e-mails ? [default: true] /// /// /// MailLogger.success.notify /// Send build success e-mails ? [default: true] /// /// /// MailLogger.failure.to /// The address to send build failure messages to. /// /// /// MailLogger.success.to /// The address to send build success messages to. /// /// /// MailLogger.failure.subject /// The subject of build failure messages. [default: "Build Failure"] /// /// /// MailLogger.success.subject /// The subject of build success messages. [default: "Build Success"] /// /// /// MailLogger.success.attachments /// The ID of a fileset holdng set of files to attach when the build is successful. /// /// /// MailLogger.failure.attachments /// The ID of a fileset holdng set of files to attach when the build fails. /// /// /// MailLogger.body.encoding /// The encoding type of the body of the e-mail message. [default: system's ANSI code page] /// /// /// MailLogger.smtp.username /// The name of the user to login to the SMTP server. /// /// /// MailLogger.smtp.password /// The password of the specified user. /// /// /// MailLogger.smtp.enablessl /// Specifies whether to use SSL to encrypt the connection. [default: false] /// /// /// MailLogger.smtp.port /// The SMTP server port to connect to. [default: 25] /// /// /// [Serializable()] public class MailLogger : DefaultLogger { #region Public Instance Constructors /// /// Initializes a new instance of the /// class. /// public MailLogger() : base() { } #endregion Public Instance Constructors #region Override implementation of DefaultLogger /// /// Signals that a build has started. /// /// The source of the event. /// A object that contains the event data. /// /// This event is fired before any targets have started. /// public override void BuildStarted(object sender, BuildEventArgs e) { base.BuildStarted (sender, e); // add an item to the project stack _projectStack.Push(null); } /// /// Signals that the last target has finished, and send an e-mail with /// the build results. /// /// The source of the event. /// A object that contains the event data. public override void BuildFinished(object sender, BuildEventArgs e) { #if (NET_1_1) const string cdoNamespaceURI = "http://schemas.microsoft.com/cdo/configuration/"; #endif base.BuildFinished(sender, e); // remove an item from the project stack _projectStack.Pop(); // check if there are still nested projects executing if (_projectStack.Count != 0) { // do not yet send the mail, as it should only be sent when the // main project is finished return; } Encoding bodyEncoding = null; Project project = e.Project; PropertyDictionary properties = project.Properties; bool success = (e.Exception == null); string prefix = success ? "success" : "failure"; try { string propertyValue = GetPropertyValue(properties, prefix + ".notify", "true", false); bool notify = true; try { notify = Convert.ToBoolean(propertyValue, CultureInfo.InvariantCulture); } catch { notify = true; } propertyValue = GetPropertyValue(properties, "body.encoding", null, false); try { if (propertyValue != null) { bodyEncoding = Encoding.GetEncoding(propertyValue); } } catch { // ignore invalid encoding } if (!notify) { return; } // create message to send MailMessage mailMessage = new MailMessage(); mailMessage.From = GetPropertyValue(properties, "from", null, true); mailMessage.To = GetPropertyValue(properties, prefix + ".to", null, true); mailMessage.Subject = GetPropertyValue(properties, prefix + ".subject", (success) ? "Build Success" : "Build Failure", false); mailMessage.Body = _buffer.ToString(); string smtpUsername = GetPropertyValue(properties, "smtp.username", null, false); if (smtpUsername != null) { #if (NET_1_1) mailMessage.Fields[cdoNamespaceURI + "smtpauthenticate"] = 1; mailMessage.Fields[cdoNamespaceURI + "sendusername"] = smtpUsername; #else Console.Error.WriteLine("[MailLogger] MailLogger.smtp.username" + " is not supported if NAnt is built targeting .NET" + " Framework 1.0."); #endif } string smtpPassword = GetPropertyValue(properties, "smtp.password", null, false); if (smtpPassword == null) { #if (NET_1_1) mailMessage.Fields[cdoNamespaceURI + "sendpassword"] = smtpPassword; #else Console.Error.WriteLine("[MailLogger] MailLogger.smtp.password" + " is not supported if NAnt is built targeting .NET" + " Framework 1.0."); #endif } string smtpPort = GetPropertyValue(properties, "smtp.port", null, false); if (smtpPort != null) { #if (NET_1_1) mailMessage.Fields[cdoNamespaceURI + "smtpserverport"] = smtpPort; #else Console.Error.WriteLine("[MailLogger] MailLogger.smtp.port" + " is not supported if NAnt is built targeting .NET" + " Framework 1.0."); #endif } string enableSSL = GetPropertyValue(properties, "smtp.enablessl", null, false); if (enableSSL != null) { #if (NET_1_1) mailMessage.Fields[cdoNamespaceURI + "smtpusessl"] = enableSSL; #else Console.Error.WriteLine("[MailLogger] MailLogger.smtp.enablessl" + " is not supported if NAnt is built targeting .NET" + " Framework 1.0."); #endif } // attach files in fileset to message AttachFiles(mailMessage, project, GetPropertyValue(properties, prefix + ".attachments", null, false)); // set encoding of body if (bodyEncoding != null) { mailMessage.BodyEncoding = bodyEncoding; } // send the message SmtpMail.SmtpServer = GetPropertyValue(properties, "mailhost", "localhost", false); SmtpMail.Send(mailMessage); } catch (Exception ex) { Console.Error.WriteLine("[MailLogger] E-mail could not be sent!"); Console.Error.WriteLine(ex.ToString()); } } /// /// Receives and buffers log messages. /// /// The message being logged. protected override void Log(string message) { _buffer.Append(message).Append(Environment.NewLine); } #endregion Override implementation of DefaultLogger #region Private Instance Methods /// /// Gets the value of the specified property. /// /// Properties to obtain value from. /// Suffix of property name. "MailLogger" will be prepended internally. /// Value returned if property is not present in . /// Value indicating whether the property should exist, or have a default value set. /// /// The value of the specified property; or the default value if the /// property is not present in . /// /// is , and the specified property is not present and no default value has been given. private string GetPropertyValue(PropertyDictionary properties, string name, string defaultValue, bool required) { string propertyName = "MailLogger." + name; string value = (string) properties[propertyName]; if (value == null) { value = defaultValue; } if (required && value == null) { throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "Missing required parameter {0}.", propertyName)); } return value; } private void AttachFiles(MailMessage mail, Project project, string filesetID) { if (StringUtils.IsNullOrEmpty(filesetID)) { return; } // lookup fileset FileSet fileset = project.DataTypeReferences[filesetID] as FileSet; if (fileset == null) { Console.Error.WriteLine("[MailLogger] Fileset \"{0}\" is not" + " defined. No files have been attached.", filesetID); return; } foreach (string fileName in fileset.FileNames) { if (!File.Exists(fileName)) { Console.Error.WriteLine("[MailLogger] Attachment \"{0}\"" + " does not exist. Skipping.", filesetID); continue; } // create attachment MailAttachment attachment = new MailAttachment(fileName, MailEncoding.UUEncode); // add attachment to mail mail.Attachments.Add(attachment); } } #endregion Private Instance Methods #region Private Instance Fields /// /// Buffer in which the message is constructed prior to sending. /// private StringBuilder _buffer = new StringBuilder(); /// /// Holds the stack of currently executing projects. /// private Stack _projectStack = new Stack(); #endregion Private Instance Fields } /// /// Contains a strongly typed collection of /// objects. /// [Serializable()] public class BuildListenerCollection : CollectionBase { #region Public Instance Constructors /// /// Initializes a new instance of the /// class. /// public BuildListenerCollection() { } /// /// Initializes a new instance of the /// class with the specified instance. /// public BuildListenerCollection(BuildListenerCollection value) { AddRange(value); } /// /// Initializes a new instance of the /// class with the specified array of instances. /// public BuildListenerCollection(IBuildListener[] value) { AddRange(value); } #endregion Public Instance Constructors #region Public Instance Properties /// /// Gets or sets the element at the specified index. /// /// The zero-based index of the element to get or set. [System.Runtime.CompilerServices.IndexerName("Item")] public IBuildListener this[int index] { get { return ((IBuildListener)(base.List[index])); } set { base.List[index] = value; } } #endregion Public Instance Properties #region Public Instance Methods /// /// Adds a to the end of the collection. /// /// The to be added to the end of the collection. /// The position into which the new element was inserted. public int Add(IBuildListener item) { return base.List.Add(item); } /// /// Adds the elements of a array to the end of the collection. /// /// The array of elements to be added to the end of the collection. public void AddRange(IBuildListener[] items) { for (int i = 0; (i < items.Length); i = (i + 1)) { Add(items[i]); } } /// /// Adds the elements of a to the end of the collection. /// /// The to be added to the end of the collection. public void AddRange(BuildListenerCollection items) { for (int i = 0; (i < items.Count); i = (i + 1)) { Add(items[i]); } } /// /// Determines whether a is in the collection. /// /// The to locate in the collection. /// /// if is found in the /// collection; otherwise, . /// public bool Contains(IBuildListener item) { return base.List.Contains(item); } /// /// Copies the entire collection to a compatible one-dimensional array, starting at the specified index of the target array. /// /// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing. /// The zero-based index in at which copying begins. public void CopyTo(IBuildListener[] array, int index) { base.List.CopyTo(array, index); } /// /// Retrieves the index of a specified object in the collection. /// /// The object for which the index is returned. /// /// The index of the specified . If the is not currently a member of the collection, it returns -1. /// public int IndexOf(IBuildListener item) { return base.List.IndexOf(item); } /// /// Inserts a into the collection at the specified index. /// /// The zero-based index at which should be inserted. /// The to insert. public void Insert(int index, IBuildListener item) { base.List.Insert(index, item); } /// /// Returns an enumerator that can iterate through the collection. /// /// /// A for the entire collection. /// public new BuildListenerEnumerator GetEnumerator() { return new BuildListenerEnumerator(this); } /// /// Removes a member from the collection. /// /// The to remove from the collection. public void Remove(IBuildListener item) { base.List.Remove(item); } #endregion Public Instance Methods } /// /// Enumerates the elements of a . /// public class BuildListenerEnumerator : IEnumerator { #region Internal Instance Constructors /// /// Initializes a new instance of the class /// with the specified . /// /// The collection that should be enumerated. internal BuildListenerEnumerator(BuildListenerCollection arguments) { IEnumerable temp = (IEnumerable) (arguments); _baseEnumerator = temp.GetEnumerator(); } #endregion Internal Instance Constructors #region Implementation of IEnumerator /// /// Gets the current element in the collection. /// /// /// The current element in the collection. /// public IBuildListener Current { get { return (IBuildListener) _baseEnumerator.Current; } } object IEnumerator.Current { get { return _baseEnumerator.Current; } } /// /// Advances the enumerator to the next element of the collection. /// /// /// if the enumerator was successfully advanced /// to the next element; if the enumerator has /// passed the end of the collection. /// public bool MoveNext() { return _baseEnumerator.MoveNext(); } bool IEnumerator.MoveNext() { return _baseEnumerator.MoveNext(); } /// /// Sets the enumerator to its initial position, which is before the /// first element in the collection. /// public void Reset() { _baseEnumerator.Reset(); } void IEnumerator.Reset() { _baseEnumerator.Reset(); } #endregion Implementation of IEnumerator #region Private Instance Fields private IEnumerator _baseEnumerator; #endregion Private Instance Fields } /// /// Implements a for writing information to /// the NAnt logging infrastructure. /// public class LogWriter : TextWriter { #region Public Instance Constructors /// /// Initializes a new instance of the class /// for the specified with the specified output /// level and format provider. /// /// Determines the indentation level. /// The with which messages will be output to the build log. /// An object that controls formatting. public LogWriter(Task task, Level outputLevel, IFormatProvider formatProvider) : base(formatProvider) { _task = task; _outputLevel = outputLevel; } #endregion Public Instance Constructors #region Override implementation of TextWriter /// /// Gets the in which the output is written. /// /// /// The always writes output in UTF8 /// encoding. /// public override Encoding Encoding { get { return Encoding.UTF8; } } /// /// Writes a character array to the buffer. /// /// The character array to write to the text stream. public override void Write(char[] chars) { Write(new string(chars, 0, chars.Length -1)); } /// /// Writes a string to the buffer. /// /// public override void Write(string value) { _message += value; } /// /// Writes an empty string to the logging infrastructure. /// public override void WriteLine() { WriteLine(string.Empty); } /// /// Writes a string to the logging infrastructure. /// /// The string to write. If is a null reference, only the line termination characters are written. public override void WriteLine(string value) { _message += value; Flush(); } /// /// Writes out a formatted string using the same semantics as /// . /// /// The formatting string. /// The object array to write into format string. public override void WriteLine(string line, params object[] args) { _message += string.Format(CultureInfo.InvariantCulture, line, args); Flush(); } /// /// Causes any buffered data to be written to the logging infrastructure. /// public override void Flush() { if (_message.Length != 0) { _task.Log(OutputLevel, _message); _message = string.Empty; } } /// /// Closes the current writer and releases any system resources /// associated with the writer. /// public override void Close() { Flush(); base.Close(); } #endregion Override implementation of TextWriter #region Override implementation of MarshalByRefObject /// /// Obtains a lifetime service object to control the lifetime policy for /// this instance. /// /// /// An object of type used to control the lifetime /// policy for this instance. This is the current lifetime service object /// for this instance if one exists; otherwise, a new lifetime service /// object initialized with a lease that will never time out. /// public override Object InitializeLifetimeService() { ILease lease = (ILease) base.InitializeLifetimeService(); if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.Zero; } return lease; } #endregion Override implementation of MarshalByRefObject #region Protected Instance Properties /// /// Gets the with which messages will be output to /// the build log. /// protected Level OutputLevel { get { return _outputLevel; } } #endregion Protected Instance Properties #region Private Instance Fields private Task _task; private Level _outputLevel; private string _message = string.Empty; #endregion Private Instance Fields } }