In class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine:
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ConsoleLogger.cs
1 //
2 // ConsoleLogger.cs: Outputs to the console
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 // 
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 #if NET_2_0
29
30 using System;
31 using System.Runtime.InteropServices;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Security;
36 using System.Text;
37 using Microsoft.Build.Framework;
38
39 namespace Microsoft.Build.BuildEngine {
40         public class ConsoleLogger : ILogger {
41         
42                 string          parameters;
43                 int             indent;
44                 LoggerVerbosity verbosity;
45                 WriteHandler    writeHandler;
46                 int             errorCount;
47                 int             warningCount;
48                 DateTime                buildStart;
49                 bool            performanceSummary;
50                 bool            showSummary;
51                 bool            skipProjectStartedText;
52                 List<string> errors, warnings;
53                 bool            projectFailed;
54                 ConsoleColor errorColor, warningColor, eventColor, messageColor, highMessageColor;
55                 ColorSetter colorSet;
56                 ColorResetter colorReset;
57                 bool no_message_color, use_colors;
58                 bool noItemAndPropertyList;
59
60                 List<BuildStatusEventArgs> events;
61                 Dictionary<string, List<string>> errorsTable;
62                 Dictionary<string, List<string>> warningsTable;
63                 string current_events_string;
64                 
65                 public ConsoleLogger ()
66                         : this (LoggerVerbosity.Normal, null, null, null)
67                 {
68                 }
69
70                 public ConsoleLogger (LoggerVerbosity verbosity)
71                         : this (LoggerVerbosity.Normal, null, null, null)
72                 {
73                 }
74                 
75                 public ConsoleLogger (LoggerVerbosity verbosity,
76                                       WriteHandler write,
77                                       ColorSetter colorSet,
78                                       ColorResetter colorReset)
79                 {
80                         this.verbosity = verbosity;
81                         this.indent = 0;
82                         this.errorCount = 0;
83                         this.warningCount = 0;
84                         if (write == null)
85                                 this.writeHandler += new WriteHandler (WriteHandlerFunction);
86                         else
87                                 this.writeHandler += write;
88                         this.performanceSummary = false;
89                         this.showSummary = true;
90                         this.skipProjectStartedText = false;
91                         errors = new List<string> ();
92                         warnings = new List<string> ();
93                         this.colorSet = colorSet;
94                         this.colorReset = colorReset;
95
96                         events = new List<BuildStatusEventArgs> ();
97                         errorsTable = new Dictionary<string, List<string>> ();
98                         warningsTable = new Dictionary<string, List<string>> ();
99
100                         //defaults
101                         errorColor = ConsoleColor.DarkRed;
102                         warningColor = ConsoleColor.DarkYellow;
103                         eventColor = ConsoleColor.DarkCyan;
104                         messageColor = ConsoleColor.DarkGray;
105                         highMessageColor = ConsoleColor.White;
106
107                         // if message color is not set via the env var,
108                         // then don't use any color for it.
109                         no_message_color = true;
110
111                         use_colors = false;
112                         if (colorSet == null || colorReset == null)
113                                 return;
114
115                         // color support
116                         string config = Environment.GetEnvironmentVariable ("XBUILD_COLORS");
117                         if (config == null) {
118                                 use_colors = true;
119                                 return;
120                         }
121
122                         if (config == "disable")
123                                 return;
124
125                         use_colors = true;
126                         string [] pairs = config.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
127                         foreach (string pair in pairs) {
128                                 string [] parts = pair.Split (new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
129                                 if (parts.Length != 2)
130                                         continue;
131
132                                 if (parts [0] == "errors")
133                                         TryParseConsoleColor (parts [1], ref errorColor);
134                                 else if (parts [0] == "warnings")
135                                         TryParseConsoleColor (parts [1], ref warningColor);
136                                 else if (parts [0] == "events")
137                                         TryParseConsoleColor (parts [1], ref eventColor);
138                                 else if (parts [0] == "messages") {
139                                         if (TryParseConsoleColor (parts [1], ref messageColor)) {
140                                                 highMessageColor = GetBrightColorFor (messageColor);
141                                                 no_message_color = false;
142                                         }
143                                 }
144                         }
145                 }
146
147                 bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
148                 {
149                         switch (color_str.ToLower ()) {
150                         case "black": color = ConsoleColor.Black; break;
151
152                         case "blue": color = ConsoleColor.DarkBlue; break;
153                         case "green": color = ConsoleColor.DarkGreen; break;
154                         case "cyan": color = ConsoleColor.DarkCyan; break;
155                         case "red": color = ConsoleColor.DarkRed; break;
156                         case "magenta": color = ConsoleColor.DarkMagenta; break;
157                         case "yellow": color = ConsoleColor.DarkYellow; break;
158                         case "grey": color = ConsoleColor.DarkGray; break;
159
160                         case "brightgrey": color = ConsoleColor.Gray; break;
161                         case "brightblue": color = ConsoleColor.Blue; break;
162                         case "brightgreen": color = ConsoleColor.Green; break;
163                         case "brightcyan": color = ConsoleColor.Cyan; break;
164                         case "brightred": color = ConsoleColor.Red; break;
165                         case "brightmagenta": color = ConsoleColor.Magenta; break;
166                         case "brightyellow": color = ConsoleColor.Yellow; break;
167
168                         case "white":
169                         case "brightwhite": color = ConsoleColor.White; break;
170                         default: return false;
171                         }
172
173                         return true;
174                 }
175
176                 ConsoleColor GetBrightColorFor (ConsoleColor color)
177                 {
178                         switch (color) {
179                         case ConsoleColor.DarkBlue: return ConsoleColor.Blue;
180                         case ConsoleColor.DarkGreen: return ConsoleColor.Green;
181                         case ConsoleColor.DarkCyan: return ConsoleColor.Cyan;
182                         case ConsoleColor.DarkRed: return ConsoleColor.Red;
183                         case ConsoleColor.DarkMagenta: return ConsoleColor.Magenta;
184                         case ConsoleColor.DarkYellow: return ConsoleColor.Yellow;
185                         case ConsoleColor.DarkGray: return ConsoleColor.Gray;
186                         case ConsoleColor.Gray: return ConsoleColor.White;
187
188                         default: return color;
189                         }
190                 }
191                 
192                 public void ApplyParameter (string parameterName,
193                                             string parameterValue)
194                 {
195                         // FIXME: what we should do here? in msbuild it isn't
196                         // changing "parameters" property
197                 }
198
199                 public virtual void Initialize (IEventSource eventSource)
200                 {
201                         eventSource.BuildStarted +=  new BuildStartedEventHandler (BuildStartedHandler);
202                         eventSource.BuildFinished += new BuildFinishedEventHandler (BuildFinishedHandler);
203                         eventSource.ProjectStarted += new ProjectStartedEventHandler (ProjectStartedHandler);
204                         eventSource.ProjectFinished += new ProjectFinishedEventHandler (ProjectFinishedHandler);
205                         eventSource.TargetStarted += new TargetStartedEventHandler (TargetStartedHandler);
206                         eventSource.TargetFinished += new TargetFinishedEventHandler (TargetFinishedHandler);
207                         eventSource.TaskStarted += new TaskStartedEventHandler (TaskStartedHandler);
208                         eventSource.TaskFinished += new TaskFinishedEventHandler (TaskFinishedHandler);
209                         eventSource.MessageRaised += new BuildMessageEventHandler (MessageHandler);
210                         eventSource.WarningRaised += new BuildWarningEventHandler (WarningHandler);
211                         eventSource.ErrorRaised += new BuildErrorEventHandler (ErrorHandler);
212                 }
213                 
214                 public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
215                 {
216                         WriteLine (String.Empty);
217                         WriteLine (String.Format ("Build started {0}.", args.Timestamp));
218                         WriteLine ("__________________________________________________");
219                         buildStart = args.Timestamp;
220
221                         PushEvent (args);
222                 }
223                 
224                 public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
225                 {
226                         if (args.Succeeded == true && !projectFailed) {
227                                 WriteLine ("Build succeeded.");
228                         } else {
229                                 WriteLine ("Build FAILED.");
230                         }
231                         if (performanceSummary == true) {
232                         }
233
234                         if (warnings.Count > 0) {
235                                 WriteLine (Environment.NewLine + "Warnings:");
236                                 SetColor (warningColor);
237
238                                 WriteLine (String.Empty);
239                                 foreach (KeyValuePair<string, List<string>> pair in warningsTable) {
240                                         if (!String.IsNullOrEmpty (pair.Key))
241                                                 WriteLine (pair.Key);
242
243                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
244                                         foreach (string msg in pair.Value)
245                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
246
247                                         WriteLine (String.Empty);
248                                 }
249
250                                 ResetColor ();
251                         }
252
253                         if (errors.Count > 0) {
254                                 WriteLine ("Errors:");
255                                 SetColor (errorColor);
256
257                                 WriteLine (String.Empty);
258                                 foreach (KeyValuePair<string, List<string>> pair in errorsTable) {
259                                         if (!String.IsNullOrEmpty (pair.Key))
260                                                 WriteLine (pair.Key);
261
262                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
263                                         foreach (string msg in pair.Value)
264                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
265
266                                         WriteLine (String.Empty);
267                                 }
268                                 ResetColor ();
269                         }
270
271                         if (showSummary == true){
272                                 TimeSpan timeElapsed = args.Timestamp - buildStart;
273                                 WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
274                                 WriteLine (String.Format ("\t {0} Error(s)", errorCount));
275                                 WriteLine (String.Empty);
276                                 WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
277                         } 
278                         PopEvent ();
279                 }
280
281                 public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
282                 {
283                         SetColor (eventColor);
284                         WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile,
285                                                 String.IsNullOrEmpty (args.TargetNames) ? "default" : args.TargetNames));
286                         ResetColor ();
287                         WriteLine (String.Empty);
288                         DumpProperties (args.Properties);
289                         DumpItems (args.Items);
290                         PushEvent (args);
291                 }
292                 
293                 public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
294                 {
295                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
296                                 if (indent == 1)
297                                         indent --;
298                                 SetColor (eventColor);
299                                 WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
300                                                         args.Succeeded ? String.Empty : "-- FAILED"));
301                                 ResetColor ();
302                                 WriteLine (String.Empty);
303                         }
304                         if (!projectFailed)
305                                 // no project has failed yet, so update the flag
306                                 projectFailed = !args.Succeeded;
307
308                         PopEvent ();
309                 }
310                 
311                 public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
312                 {
313                         indent++;
314                         SetColor (eventColor);
315                         WriteLine (String.Format ("Target {0}:",args.TargetName));
316                         ResetColor ();
317                         PushEvent (args);
318                 }
319                 
320                 public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
321                 {
322                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) || !args.Succeeded) {
323                                 SetColor (eventColor);
324                                 WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
325                                         args.TargetName, args.ProjectFile,
326                                         args.Succeeded ? String.Empty : "-- FAILED"));
327                                 ResetColor ();
328                         }
329                         indent--;
330
331                         WriteLine (String.Empty);
332                         PopEvent ();
333                 }
334                 
335                 public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
336                 {
337                         if (this.verbosity == LoggerVerbosity.Detailed) {
338                                 SetColor (eventColor);
339                                 WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
340                                 ResetColor ();
341                         }
342                         indent++;
343                         PushEvent (args);
344                 }
345                 
346                 public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
347                 {
348                         indent--;
349                         if (this.verbosity == LoggerVerbosity.Detailed || !args.Succeeded) {
350                                 SetColor (eventColor);
351                                 if (args.Succeeded)
352                                         WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
353                                 else
354                                         WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
355                                 ResetColor ();
356                         }
357                         PopEvent ();
358                 }
359                 
360                 public void MessageHandler (object sender, BuildMessageEventArgs args)
361                 {
362                         if (IsMessageOk (args)) {
363                                 if (no_message_color) {
364                                         WriteLine (args.Message);
365                                 } else {
366                                         SetColor (args.Importance == MessageImportance.High ? highMessageColor : messageColor);
367                                         WriteLine (args.Message);
368                                         ResetColor ();
369                                 }
370                         }
371                 }
372                 
373                 public void WarningHandler (object sender, BuildWarningEventArgs args)
374                 {
375                         string msg = FormatWarningEvent (args);
376                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
377                                 SetColor (warningColor);
378                                 WriteLineWithoutIndent (msg);
379                                 ResetColor ();
380                         }
381                         warnings.Add (msg);
382
383                         List<string> list = null;
384                         if (!warningsTable.TryGetValue (EventsAsString, out list))
385                                 warningsTable [EventsAsString] = list = new List<string> ();
386                         list.Add (msg);
387
388                         warningCount++;
389                 }
390                 
391                 public void ErrorHandler (object sender, BuildErrorEventArgs args)
392                 {
393                         string msg = FormatErrorEvent (args);
394                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
395                                 SetColor (errorColor);
396                                 WriteLineWithoutIndent (msg);
397                                 ResetColor ();
398                         }
399                         errors.Add (msg);
400
401                         List<string> list = null;
402                         if (!errorsTable.TryGetValue (EventsAsString, out list))
403                                 errorsTable [EventsAsString] = list = new List<string> ();
404                         list.Add (msg);
405                         errorCount++;
406                 }
407                 
408                 [MonoTODO]
409                 public void CustomEventHandler (object sender, CustomBuildEventArgs args)
410                 {
411                 }
412
413                 private void WriteLine (string message)
414                 {
415                         if (indent > 0) {
416                                 StringBuilder sb = new StringBuilder ();
417                                 for (int i = 0; i < indent; i++)
418                                         sb.Append ('\t');
419                                 sb.Append (message);
420
421                                 writeHandler (sb.ToString ());
422                         } else {
423                                 writeHandler (message);
424                         }
425                 }
426
427                 void PushEvent (BuildStatusEventArgs args)
428                 {
429                         events.Add (args);
430                         current_events_string = null;
431                 }
432
433                 void PopEvent ()
434                 {
435                         events.RemoveAt (events.Count - 1);
436                         current_events_string = null;
437                 }
438
439                 string EventsToString ()
440                 {
441                         StringBuilder sb = new StringBuilder ();
442
443                         string last_imported_target_file = String.Empty;
444                         for (int i = 0; i < events.Count; i ++) {
445                                 BuildStatusEventArgs args = events [i];
446                                 ProjectStartedEventArgs pargs = args as ProjectStartedEventArgs;
447                                 if (pargs != null) {
448                                         sb.AppendFormat ("{0} ({1}) ->\n", pargs.ProjectFile,
449                                                         String.IsNullOrEmpty (pargs.TargetNames) ?
450                                                                 "default targets" :
451                                                                 pargs.TargetNames);
452                                         last_imported_target_file = String.Empty;
453                                         continue;
454                                 }
455
456                                 TargetStartedEventArgs targs = args as TargetStartedEventArgs;
457                                 if (targs != null) {
458                                         if (targs.TargetFile != targs.ProjectFile && targs.TargetFile != last_imported_target_file)
459                                                 // target from an imported file,
460                                                 // and it hasn't been mentioned as yet
461                                                 sb.AppendFormat ("{0} ", targs.TargetFile);
462
463                                         last_imported_target_file = targs.TargetFile;
464                                         sb.AppendFormat ("({0} target) ->\n", targs.TargetName);
465                                 }
466                         }
467
468                         return sb.ToString ();
469                 }
470                 
471                 private void WriteLineWithoutIndent (string message)
472                 {
473                         writeHandler (message);
474                 }
475                 
476                 private void WriteHandlerFunction (string message)
477                 {
478                         Console.WriteLine (message);
479                 }
480
481                 void SetColor (ConsoleColor color)
482                 {
483                         if (use_colors)
484                                 colorSet (color);
485                 }
486
487                 void ResetColor ()
488                 {
489                         if (use_colors)
490                                 colorReset ();
491                 }
492                 
493                 private void ParseParameters ()
494                 {
495                         string[] splittedParameters = parameters.Split (';');
496                         foreach (string s in splittedParameters ) {
497                                 switch (s) {
498                                 case "PerformanceSummary":
499                                         this.performanceSummary = true;
500                                         break;
501                                 case "NoSummary":
502                                         this.showSummary = false;
503                                         break;
504                                 case "NoItemAndPropertyList":
505                                         this.noItemAndPropertyList = true;
506                                         break;
507                                 default:
508                                         throw new ArgumentException ("Invalid parameter : " + s);
509                                 }
510                         }
511                 }
512                 
513                 public virtual void Shutdown ()
514                 {
515                 }
516
517                 static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
518                 
519                 private string FormatErrorEvent (BuildErrorEventArgs args)
520                 {
521                         // For some reason we get an 1-char empty string as Subcategory somtimes.
522                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
523                         string subcat = subprefix == "" ? "" : args.Subcategory;
524                                 
525                         if (args.LineNumber != 0){
526                                 if (args.ColumnNumber != 0 && !InEmacs) 
527                                         return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
528                                                               args.File, args.LineNumber, args.ColumnNumber,
529                                                               subprefix, subcat, args.Code, args.Message);
530
531                                 return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
532                                                       args.File, args.LineNumber,
533                                                       subprefix, subcat, args.Code, args.Message);
534                         } else {
535                                 return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
536                                         args.Message);
537                         }
538                 }
539
540                 private string FormatWarningEvent (BuildWarningEventArgs args)
541                 {
542                         // For some reason we get an 1-char empty string as Subcategory somtimes.
543                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
544                         string subcat = subprefix == "" ? "" : args.Subcategory;
545
546                         // FIXME: show more complicated args
547                         if (args.LineNumber != 0){
548
549                                 if (args.ColumnNumber != 0 && !InEmacs) {
550                                         return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
551                                                               args.File, args.LineNumber, args.ColumnNumber,
552                                                               subprefix, subcat, args.Code, args.Message);
553                                 }
554                                 return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
555                                                       args.File, args.LineNumber,
556                                                       subprefix, subcat, args.Code, args.Message);
557                         } else {
558                                 return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
559                                         args.Message);
560                         }
561                 }
562                 
563                 private bool IsMessageOk (BuildMessageEventArgs bsea)
564                 {
565                         if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
566                                 return true;
567                         } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
568                                 return true;
569                         } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
570                                 return true;
571                         } else
572                                 return false;
573                 }
574                 
575                 private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
576                 {
577                                 if (v == LoggerVerbosity.Diagnostic) {
578                                         return LoggerVerbosity.Diagnostic <= verbosity;
579                                 } else if (v == LoggerVerbosity.Detailed) {
580                                         return LoggerVerbosity.Detailed <= verbosity;
581                                 } else if (v == LoggerVerbosity.Normal) {
582                                         return LoggerVerbosity.Normal <= verbosity;
583                                 } else if (v == LoggerVerbosity.Minimal) {
584                                         return LoggerVerbosity.Minimal <= verbosity;
585                                 } else if (v == LoggerVerbosity.Quiet) {
586                                         return true;
587                                 } else
588                                         return false;
589                 }
590
591                 void DumpProperties (IEnumerable properties)
592                 {
593                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
594                                 return;
595
596                         SetColor (eventColor);
597                         WriteLine ("\n");
598                         WriteLine ("Initial Properties:");
599                         ResetColor ();
600
601                         if (properties == null)
602                                 return;
603
604                         var dict = new SortedDictionary<string, string> ();
605                         foreach (DictionaryEntry de in properties)
606                                 dict [(string)de.Key] = (string)de.Value;
607
608                         foreach (KeyValuePair<string, string> pair in dict)
609                                 WriteLine (String.Format ("{0} = {1}", pair.Key, pair.Value));
610                         WriteLine ("\n");
611                 }
612
613                 void DumpItems (IEnumerable items)
614                 {
615                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic) || items == null)
616                                 return;
617
618                         SetColor (eventColor);
619                         WriteLine ("\n");
620                         WriteLine ("Initial Items:");
621                         ResetColor ();
622                         if (items == null)
623                                 return;
624
625                         var items_table = new SortedDictionary<string, List<ITaskItem>> ();
626                         foreach (DictionaryEntry de in items) {
627                                 string key = (string)de.Key;
628                                 if (!items_table.ContainsKey (key))
629                                         items_table [key] = new List<ITaskItem> ();
630
631                                 items_table [key].Add ((ITaskItem) de.Value);
632                         }
633
634                         foreach (string name in items_table.Keys) {
635                                 WriteLine (name);
636                                 indent ++;
637                                 foreach (ITaskItem item in items_table [name])
638                                         WriteLine (item.ItemSpec);
639                                 indent--;
640                         }
641                         WriteLine ("\n");
642                 }
643
644                 public string Parameters {
645                         get {
646                                 return parameters;
647                         }
648                         set {
649                                 if (value == null)
650                                         throw new ArgumentNullException ();
651                                 parameters = value;
652                                 if (parameters != String.Empty)
653                                         ParseParameters ();
654                         }
655                 }
656
657                 string EventsAsString {
658                         get {
659                                 if (current_events_string == null)
660                                         current_events_string = EventsToString ();
661                                 return current_events_string;
662                         }
663                 }
664                 
665                 public bool ShowSummary {
666                         get { return showSummary; }
667                         set { showSummary = value; }
668                 }
669                 
670                 public bool SkipProjectStartedText {
671                         get { return skipProjectStartedText; }
672                         set { skipProjectStartedText = value; }
673                 }
674
675                 public LoggerVerbosity Verbosity {
676                         get { return verbosity; }
677                         set { verbosity = value; }
678                 }
679
680                 protected WriteHandler WriteHandler {
681                         get { return writeHandler; }
682                         set { writeHandler = value; }
683                 }
684         }
685 }
686
687 #endif