Avoid unnecessary allocation on indexer access
[mono.git] / mcs / class / System / System.Diagnostics / DefaultTraceListener.cs
index 977fab7fd853a81d5d3896d77526777d139121e2..7a1e541ad9bfece3dcd9254c73408a40e1b614bf 100644 (file)
 // (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 {
@@ -20,15 +43,104 @@ 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 false;}
+                       get {return assertUiEnabled;}
                        set {/* ignore */}
                }
 
@@ -38,48 +150,74 @@ namespace System.Diagnostics {
                        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());
-               }
+               public override void Fail (string message)
+               {
+                       base.Fail (message);
+                       WriteLine (new StackTrace().ToString());
+               }
 
-               #if USE_NATIVE_WIN32_OUTPUT_DEBUG_STRING
+               public override void Fail (string message, string detailMessage)
+               {
+                       base.Fail (message, detailMessage);
+                       WriteLine (new StackTrace().ToString());
+               }
 
-      [DllImport ("kernel32.dll")]
-      private extern static void OutputDebugString (string message);
+#if TARGET_JVM
+               private void WriteDebugString (string message)
+               {
+#else
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private extern static void WriteWindowsDebugString (string message);
 
-               #else
+               private void WriteDebugString (string message)
+               {
+                       if (OnWin32)
+                               WriteWindowsDebugString (message);
+                       else
+#endif
+                               WriteMonoTrace (message);
+               }
 
-      private static void OutputDebugString (string message)
-      {
-        Console.Write ("**ods** " + 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;
+                       }
+               }
 
-               #endif
+               private void WritePrefix ()
+               {
+                       if (!OnWin32) {
+                               WriteMonoTrace (MonoTracePrefix);
+                       }
+               }
 
                private void WriteImpl (string message)
                {
-                       if (NeedIndent)
+                       if (NeedIndent) {
                                WriteIndent ();
+                               WritePrefix ();
+                       }
 
-                       OutputDebugString (message);
+                       WriteDebugString (message);
 
                        if (Debugger.IsLogging())
                                Debugger.Log (0, null, message);
 
-                       WriteLogFile (message);
+                       WriteLogFile (message, LogFileName);
                }
 
-               private void WriteLogFile (string message)
+               private void WriteLogFile (string message, string logFile)
                {
-                       string fname = LogFileName;
+                       string fname = logFile;
                        if (fname != null && fname.Length != 0) {
                                FileInfo info = new FileInfo (fname);
                                StreamWriter sw = null;
@@ -99,6 +237,7 @@ namespace System.Diagnostics {
 
                                using (sw) {
                                        sw.Write (message);
+                                       sw.Flush ();
                                }
                        }
                }
@@ -112,6 +251,7 @@ namespace System.Diagnostics {
                {
                        string msg = message + Environment.NewLine;
                        WriteImpl (msg);
+
                        NeedIndent = true;
                }
        }