Avoid unnecessary allocation on indexer access
[mono.git] / mcs / class / System / System.Diagnostics / DefaultTraceListener.cs
index fc0ee84a3f1d7cec5582a926b0f5f306b3a60452..7a1e541ad9bfece3dcd9254c73408a40e1b614bf 100644 (file)
-//\r
-// System.Diagnostics.DefaultTraceListener.cs\r
-//\r
-// Author:\r
-//     John R. Hicks (angryjohn69@nc.rr.com)\r
-//\r
-// (C) 2001\r
-//\r
-using System;\r
-\r
-namespace System.Diagnostics\r
-{\r
-       /// <summary>\r
-       /// Provides the default output methods and behavior for tracing.\r
-       /// </summary>\r
-       /// <remarks>\r
-       /// Since there is no debugging API ala Win32 on Mono, <see cref="System.Console.Out">\r
-       /// Console.Out</see> is being used as the default output method.\r
-       /// </remarks>\r
-       public class DefaultTraceListener : TraceListener\r
-       {       \r
-               private string logFileName;\r
-               \r
-               public DefaultTraceListener() : base("Default")\r
-               {\r
-                       logFileName = "";\r
-               }\r
-               \r
-               /// <summary>\r
-               /// Gets or sets name of a log file to write trace or debug messages to.\r
-               /// </summary>\r
-               /// <value>\r
-               /// The name of a log file to write trace or debug messages to.\r
-               /// </value>\r
-               public String LogFileName\r
-               {\r
-                       get\r
-                       {\r
-                               return logFileName;\r
-                       }\r
-                       set\r
-                       {\r
-                               logFileName = value;\r
-                       }\r
-               }\r
-               \r
-               /// <summary>\r
-               /// Emits or displays a message and a stack trace for an assertion that \r
-               /// always fails.\r
-               /// </summary>\r
-               /// <param name="message">\r
-               /// The message to emit or display.\r
-               /// </param>\r
-               public override void Fail(string message)\r
-               {\r
-                       Console.Out.WriteLine(message);\r
-                       new StackTrace().ToString();\r
-               }\r
-               \r
-               /// <summary>\r
-               /// Emits or displays detailed messages and a stack trace\r
-               /// for an assertion that always fails.\r
-               /// </summary>\r
-               /// <param name="message">\r
-               /// The message to emit or display\r
-               /// </param>\r
-               /// <param name="detailMessage">\r
-               /// The detailed message to emit or display.\r
-               /// </param>\r
-               public override void Fail(string message, string detailMessage)\r
-               {\r
-                       Console.Out.WriteLine(message + ": " + detailMessage);\r
-                       new StackTrace().ToString();\r
-               }\r
-               \r
-               /// <summary>\r
-               /// Writes the output to the Console\r
-               /// </summary>\r
-               /// <param name="message">\r
-               /// The message to write\r
-               /// </param>\r
-               public override void Write(string message)\r
-               {\r
-                       Console.Out.Write(message);\r
-               }\r
-               \r
-               /// <summary>\r
-               /// Writes the output to the Console, followed by a newline\r
-               /// </summary>\r
-               /// <param name="message">\r
-               /// The message to write\r
-               /// </param>\r
-               public override void WriteLine(string message)\r
-               {\r
-                       Console.Out.WriteLine(message);\r
-               }\r
-       }\r
-}\r
+//
+// System.Diagnostics.DefaultTraceListener.cs
+//
+// Authors:
+//   Jonathan Pryor (jonpryor@vt.edu)
+//
+// Comments from John R. Hicks <angryjohn69@nc.rr.com> original implementation 
+// can be found at: /mcs/docs/apidocs/xml/en/System.Diagnostics
+//
+// (C) 2002 Jonathan Pryor
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Diagnostics {
+
+       [ComVisible(false)]
+       public class DefaultTraceListener : TraceListener {
+
+               private static readonly bool OnWin32;
+
+               private const string ConsoleOutTrace = "Console.Out";
+               private const string ConsoleErrorTrace = "Console.Error";
+
+               private static readonly string MonoTracePrefix;
+               private static readonly string MonoTraceFile;
+
+               static DefaultTraceListener ()
+               {
+                       // Determine what platform we're on.  This impacts how where we send
+                       // messages.  On Win32 platforms (OnWin32 = true), we use the
+                       // `OutputDebugString' api.
+                       //
+                       // On Linux platforms, we use MONO_TRACE_LISTENER to figure things out.  See the
+                       // API documentation for more information on MONO_TRACE_LISTENER.
+                       OnWin32 = (Path.DirectorySeparatorChar == '\\');
+
+                       if (!OnWin32) {
+#if TARGET_JVM
+                               string trace = java.lang.System.getProperty("MONO_TRACE");
+#else
+                               // If we're running on Unix, we don't have OutputDebugString.
+                               // Instead, send output to...wherever the MONO_TRACE_LISTENER environment
+                               // variables says to.
+                               String trace = Environment.GetEnvironmentVariable("MONO_TRACE_LISTENER");
+#endif
+
+                               if (trace != null) {
+                                       string file = null;
+                                       string prefix = null;
+
+                                       if (trace.StartsWith (ConsoleOutTrace)) {
+                                               file = ConsoleOutTrace;
+                                               prefix = GetPrefix (trace, ConsoleOutTrace);
+                                       }
+                                       else if (trace.StartsWith (ConsoleErrorTrace)) {
+                                               file = ConsoleErrorTrace;
+                                               prefix = GetPrefix (trace, ConsoleErrorTrace);
+                                       }
+                                       else {
+                                               file = trace;
+
+                                               // We can't firgure out what the prefix would be, as ':' is a
+                                               // valid filename character.  Thus, arbitrary files don't support
+                                               // prefixes.
+                                               //
+                                               // I don't consider this to be a major issue.  Prefixes are useful 
+                                               // with Console.Out and Console.Error to help separate trace
+                                               // output from the actual program output.  Writing to an arbitrary
+                                               // file doesn't introduce issues with disambiguation.
+                                               prefix = "";
+                                       }
+
+                                       MonoTraceFile = file;
+                                       MonoTracePrefix = prefix;
+                               }
+                       }
+               }
+
+               /**
+                * Get the prefix for the specified variable.
+                *
+                * "Prefixes" are used in the MONO_TRACE_LISTENER variable, and specify text that
+                * should precede each message printed to the console.  The prefix is
+                * appended to the console location with a colon (':') separating them.
+                * For example, if MONO_TRACE_LISTENER is "Console.Out:** my prefix", the prefix is
+                * "** my prefix".
+                *
+                * Everything after the colon, if the colon is present, is used as the
+                * prefix.
+                *
+                * @param       var             The current MONO_TRACE_LISTENER variable
+                * @param       target  The name of the output location, e.g. "Console.Out"
+                */
+               private static string GetPrefix (string var, string target)
+               {
+                       // actually, we permit any character to separate `target' and the prefix;
+                       // we just skip over target the ':' would be.  This means that a space or
+                       // anything else would suffice, as long as it was only a single
+                       // character.
+                       if (var.Length > target.Length)
+                               return var.Substring (target.Length + 1);
+                       return "";
+               }
+
+               private string logFileName = null;
+
+               private bool assertUiEnabled = false;
+
+               public DefaultTraceListener () : base ("Default")
+               {
+               }
+
+               // It's hard to do anything with a UI when we don't have Windows.Forms...
+               [MonoTODO]
+               public bool AssertUiEnabled {
+                       get {return assertUiEnabled;}
+                       set {/* ignore */}
+               }
+
+               [MonoTODO]
+               public string LogFileName {
+                       get {return logFileName;}
+                       set {logFileName = value;}
+               }
+
+               public override void Fail (string message)
+               {
+                       base.Fail (message);
+                       WriteLine (new StackTrace().ToString());
+               }
+
+               public override void Fail (string message, string detailMessage)
+               {
+                       base.Fail (message, detailMessage);
+                       WriteLine (new StackTrace().ToString());
+               }
+
+#if TARGET_JVM
+               private void WriteDebugString (string message)
+               {
+#else
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private extern static void WriteWindowsDebugString (string message);
+
+               private void WriteDebugString (string message)
+               {
+                       if (OnWin32)
+                               WriteWindowsDebugString (message);
+                       else
+#endif
+                               WriteMonoTrace (message);
+               }
+
+               private void WriteMonoTrace (string message)
+               {
+                       switch (MonoTraceFile) {
+                       case ConsoleOutTrace:
+                               Console.Out.Write (message);
+                               break;
+                       case ConsoleErrorTrace:
+                               Console.Error.Write (message);
+                               break;
+                       default:
+                               WriteLogFile (message, MonoTraceFile);
+                               break;
+                       }
+               }
+
+               private void WritePrefix ()
+               {
+                       if (!OnWin32) {
+                               WriteMonoTrace (MonoTracePrefix);
+                       }
+               }
+
+               private void WriteImpl (string message)
+               {
+                       if (NeedIndent) {
+                               WriteIndent ();
+                               WritePrefix ();
+                       }
+
+                       WriteDebugString (message);
+
+                       if (Debugger.IsLogging())
+                               Debugger.Log (0, null, message);
+
+                       WriteLogFile (message, LogFileName);
+               }
+
+               private void WriteLogFile (string message, string logFile)
+               {
+                       string fname = logFile;
+                       if (fname != null && fname.Length != 0) {
+                               FileInfo info = new FileInfo (fname);
+                               StreamWriter sw = null;
+
+                               // Open the file
+                               try {
+                                       if (info.Exists)
+                                               sw = info.AppendText ();
+                                       else
+                                               sw = info.CreateText ();
+                               }
+                               catch {
+                                       // We weren't able to open the file for some reason.
+                                       // We can't write to the log file; so give up.
+                                       return;
+                               }
+
+                               using (sw) {
+                                       sw.Write (message);
+                                       sw.Flush ();
+                               }
+                       }
+               }
+
+               public override void Write (string message)
+               {
+                       WriteImpl (message);
+               }
+
+               public override void WriteLine (string message)
+               {
+                       string msg = message + Environment.NewLine;
+                       WriteImpl (msg);
+
+                       NeedIndent = true;
+               }
+       }
+}
+