﻿using System.Text;

namespace Microsoft.Build.Logging.StructuredLogger
{
    public class StringWriter
    {
        public static string GetString(object rootNode)
        {
            var sb = new StringBuilder();

            WriteNode(rootNode, sb, 0);

            return sb.ToString();
        }

        private static void WriteNode(object rootNode, StringBuilder sb, int indent = 0)
        {
            Indent(sb, indent);
            var text = rootNode.ToString();

            // when we injest strings we normalize on \n to save space.
            // when the strings leave our app via clipboard, bring \r\n back so that notepad works
            text = text.Replace("\n", "\r\n");

            sb.AppendLine(text);

            var treeNode = rootNode as TreeNode;
            if (treeNode != null && treeNode.HasChildren)
            {
                if (treeNode.HasChildren)
                {
                    foreach (var child in treeNode.Children)
                    {
                        WriteNode(child, sb, indent + 1);
                    }
                }
            }
        }

        private static void Indent(StringBuilder sb, int indent)
        {
            for (int i = 0; i < indent * 4; i++)
            {
                sb.Append(' ');
            }
        }
    }
}

﻿using System.Text;

namespace Microsoft.Build.Logging.StructuredLogger
{
    public class StringWriter
    {
        public static string GetString(object rootNode)
        {
            var sb = new StringBuilder();

            WriteNode(rootNode, sb, 0);

            return sb.ToString();
        }

        private static void WriteNode(object rootNode, StringBuilder sb, int indent = 0)
        {
            Indent(sb, indent);
            var text = rootNode.ToString() ?? "";

            // when we injest strings we normalize on \n to save space.
            // when the strings leave our app via clipboard, bring \r\n back so that notepad works
            text = text.Replace("\n", "\r\n");

            sb.AppendLine(text);

            var treeNode = rootNode as TreeNode;
            if (treeNode != null && treeNode.HasChildren)
            {
                if (treeNode.HasChildren)
                {
                    foreach (var child in treeNode.Children)
                    {
                        WriteNode(child, sb, indent + 1);
                    }
                }
            }
        }

        private static void Indent(StringBuilder sb, int indent)
        {
            for (int i = 0; i < indent * 4; i++)
            {
                sb.Append(' ');
            }
        }
    }
}

Fix NRE when copying nodes when the ToString() is null
Fix NRE when copying nodes when the ToString() is null

This happens more or less randomly with the following exception:

System.NullReferenceException was unhandled
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=StructuredLogger
  StackTrace:
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 23
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.WriteNode(Object rootNode, StringBuilder sb, Int32 indent) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 32
       at Microsoft.Build.Logging.StructuredLogger.StringWriter.GetString(Object rootNode) in C:\MSBuildStructuredLog\src\StructuredLogger\Serialization\StringWriter.cs:line 11
       at StructuredLogViewer.Controls.BuildControl.Copy()
       at StructuredLogViewer.Controls.BuildControl.TreeView_KeyDown(Object sender, KeyEventArgs args)
       at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
       at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
       at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
       at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
       at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
       at System.Windows.Input.InputManager.ProcessStagingArea()
       at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
       at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
       at System.Windows.Interop.HwndKeyboardInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawKeyboardActions actions, Int32 scanCode, Boolean isExtendedKey, Boolean isSystemKey, Int32 virtualKey)
       at System.Windows.Interop.HwndKeyboardInputProvider.ProcessKeyAction(MSG& msg, Boolean& handled)
       at System.Windows.Interop.HwndSource.CriticalTranslateAccelerator(MSG& msg, ModifierKeys modifiers)
       at System.Windows.Interop.HwndSource.OnPreprocessMessage(Object param)
       at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
       at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
       at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
       at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
       at System.Windows.Interop.HwndSource.OnPreprocessMessageThunk(MSG& msg, Boolean& handled)
       at System.Windows.Interop.ComponentDispatcherThread.RaiseThreadMessage(MSG& msg)
       at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
       at System.Windows.Application.RunDispatcher(Object ignore)
       at System.Windows.Application.RunInternal(Window window)
       at StructuredLogViewer.Entrypoint.Main(String[] args)
  InnerException:
