File: Microsoft\Tools\ServiceModel\SvcUtil\ToolConsole.cs
Web Access
Project: src\src\svcutilcore\src\dotnet-svcutil.xmlserializer.csproj (dotnet-svcutil.xmlserializer)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
namespace Microsoft.Tools.ServiceModel.SvcUtil.XmlSerializer
{
    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.CodeDom.Compiler;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Reflection;
    using System.Globalization;
 
    internal static class ToolConsole
    {
#if DEBUG
        private static bool s_debug = false;
#endif
 
#if DEBUG
        internal static void SetOptions(bool debug)
        {
            ToolConsole.s_debug = debug;
        }
#endif
 
        internal static void WriteLine(string str, int indent)
        {
            ToolStringBuilder toolStringBuilder = new ToolStringBuilder(indent);
            toolStringBuilder.WriteParagraph(str);
        }
 
        internal static void WriteLine(string str)
        {
            Console.WriteLine(str);
        }
 
        internal static void WriteLine()
        {
            Console.WriteLine();
        }
 
        internal static void WriteError(string errMsg)
        {
            WriteError(errMsg, SR.Format(SR.Error));
        }
 
        private static void WriteError(Exception e)
        {
            WriteError(e, SR.Format(SR.Error));
        }
 
        internal static void WriteUnexpectedError(Exception e)
        {
            WriteError(SR.Format(SR.ErrUnexpectedError));
            WriteError(e);
        }
 
        internal static void WriteUnexpectedError(string errMsg)
        {
            WriteError(SR.Format(SR.ErrUnexpectedError));
            if (!string.IsNullOrEmpty(errMsg))
                WriteError(errMsg);
        }
 
        internal static void WriteToolError(ToolArgumentException ae)
        {
            WriteError(ae);
 
            ToolConsole.WriteLine(SR.Format(SR.MoreHelp, Options.Abbr.Help));
        }
 
        internal static void WriteToolError(ToolRuntimeException re)
        {
            WriteError(re);
        }
 
        internal static void WriteWarning(string message)
        {
            Console.Error.Write(SR.Format(SR.Warning));
            Console.Error.WriteLine(message);
            Console.Error.WriteLine();
        }
 
        private static void WriteError(string errMsg, string prefix)
        {
            Console.Error.Write(prefix);
            Console.Error.WriteLine(errMsg);
            Console.Error.WriteLine();
        }
 
        internal static void WriteError(Exception e, string prefix)
        {
#if DEBUG
            if (s_debug)
            {
                ToolConsole.WriteLine();
                WriteError(e.ToString(), prefix);
                return;
            }
#endif
 
            WriteError(e.Message, prefix);
 
            while (e.InnerException != null)
            {
                if (e.Message != e.InnerException.Message)
                {
                    WriteError(e.InnerException.Message, "    ");
                }
                e = e.InnerException;
            }
        }
 
        internal static void WriteHeader()
        {
            // Using CommonResStrings.WcfTrademarkForCmdLine for the trademark: the proper resource for command line tools.
            ToolConsole.WriteLine(SR.Format(SR.Logo, ThisAssembly.InformationalVersion, SR.WcfTrademarkForCmdLine, SR.CopyrightForCmdLine));
        }
 
        internal static void WriteHelpText()
        {
            HelpGenerator.WriteUsage();
            ToolConsole.WriteLine();
            ToolConsole.WriteLine();
            HelpGenerator.WriteCommonOptionsHelp();
            ToolConsole.WriteLine();
            ToolConsole.WriteLine();
            HelpGenerator.WriteXmlSerializerTypeGenerationHelp();
            ToolConsole.WriteLine();
            ToolConsole.WriteLine();
            HelpGenerator.WriteExamples();
            ToolConsole.WriteLine();
            ToolConsole.WriteLine();
        }
 
        private static class HelpGenerator
        {
            private static ToolStringBuilder s_exampleBuilder = new ToolStringBuilder(4);
            static HelpGenerator() { } // beforefieldInit
 
            internal static void WriteUsage()
            {
                ToolConsole.WriteLine(SR.Format(SR.HelpUsage1));
                ToolConsole.WriteLine();
                ToolConsole.WriteLine(SR.Format(SR.HelpUsage6));
            }
 
            internal static void WriteXmlSerializerTypeGenerationHelp()
            {
                HelpCategory helpCategory = new HelpCategory(SR.Format(SR.HelpXmlSerializerTypeGenerationCategory));
 
                helpCategory.Description = SR.Format(SR.HelpXmlSerializerTypeGenerationDescription, ThisAssembly.Title);
                helpCategory.Syntax = SR.Format(SR.HelpXmlSerializerTypeGenerationSyntax, ThisAssembly.Title, SR.Format(SR.HelpInputAssemblyPath));
 
                helpCategory.Inputs = new ArgumentInfo[1];
 
                helpCategory.Inputs[0] = ArgumentInfo.CreateInputHelpInfo(SR.Format(SR.HelpInputAssemblyPath));
                helpCategory.Inputs[0].HelpText = SR.Format(SR.HelpXmlSerializerTypeGenerationSyntaxInput1);
 
                helpCategory.Options = new ArgumentInfo[3];
 
                helpCategory.Options[0] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Reference, SR.Format(SR.ParametersReference));
                helpCategory.Options[0].HelpText = SR.Format(SR.HelpXmlSerializerTypeGenerationSyntaxInput2, Options.Abbr.Reference);
 
                helpCategory.Options[1] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.ExcludeType, SR.Format(SR.ParametersExcludeType));
                helpCategory.Options[1].HelpText = SR.Format(SR.HelpXmlSerializerTypeGenerationSyntaxInput3, Options.Abbr.ExcludeType);
 
                helpCategory.Options[2] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Out, SR.Format(SR.ParametersOut));
                helpCategory.Options[2].HelpText = SR.Format(SR.HelpXmlSerializerTypeGenerationSyntaxInput4, Options.Abbr.Out);
 
                helpCategory.WriteHelp();
            }
 
            internal static void WriteCommonOptionsHelp()
            {
                HelpCategory helpCategory = new HelpCategory(SR.Format(SR.HelpCommonOptionsCategory));
                var options = new List<ArgumentInfo>();
                ArgumentInfo option;
 
                option = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Directory, SR.Format(SR.ParametersDirectory));
                option.HelpText = SR.Format(SR.HelpDirectory, Options.Abbr.Directory);
                options.Add(option);
 
                option = ArgumentInfo.CreateFlagHelpInfo(Options.Cmd.NoLogo);
                option.HelpText = SR.Format(SR.HelpNologo);
                options.Add(option);
 
                option = ArgumentInfo.CreateFlagHelpInfo(Options.Cmd.Help);
                option.HelpText = SR.Format(SR.HelpHelp, Options.Abbr.Help);
                options.Add(option);
 
                helpCategory.Options = options.ToArray();
                helpCategory.WriteHelp();
            }
 
            internal static void WriteCodeGenerationHelp()
            {
                HelpCategory helpCategory = new HelpCategory(SR.Format(SR.HelpCodeGenerationCategory));
 
                helpCategory.Description = SR.Format(SR.HelpCodeGenerationDescription, ThisAssembly.Title);
                helpCategory.Syntax = SR.Format(SR.HelpCodeGenerationAbbreviationSyntax, ThisAssembly.Title, Options.Abbr.Target, Options.Targets.Code,
                    SR.Format(SR.HelpInputMetadataDocumentPath), SR.Format(SR.HelpInputUrl), SR.Format(SR.HelpInputEpr));
 
                helpCategory.Inputs = new ArgumentInfo[3];
 
                helpCategory.Inputs[0] = ArgumentInfo.CreateInputHelpInfo(SR.Format(SR.HelpInputMetadataDocumentPath));
                helpCategory.Inputs[0].HelpText = SR.Format(SR.HelpCodeGenerationSyntaxInput1);
 
                helpCategory.Inputs[1] = ArgumentInfo.CreateInputHelpInfo(SR.Format(SR.HelpInputUrl));
                helpCategory.Inputs[1].HelpText = SR.Format(SR.HelpCodeGenerationSyntaxInput2);
 
                helpCategory.Inputs[2] = ArgumentInfo.CreateInputHelpInfo(SR.Format(SR.HelpInputEpr));
                helpCategory.Inputs[2].HelpText = SR.Format(SR.HelpCodeGenerationSyntaxInput3);
 
                helpCategory.Options = new ArgumentInfo[26];
 
                helpCategory.Options[0] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Out, SR.Format(SR.ParametersOut));
                helpCategory.Options[0].HelpText = SR.Format(SR.HelpOut, Options.Abbr.Out);
 
                helpCategory.Options[1] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Namespace, SR.Format(SR.ParametersNamespace));
                helpCategory.Options[1].HelpText = SR.Format(SR.HelpNamespace, Options.Abbr.Namespace);
 
                helpCategory.Options[2] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.Reference, SR.Format(SR.ParametersReference));
                helpCategory.Options[2].BeginGroup = true;
                helpCategory.Options[2].HelpText = SR.Format(SR.HelpReferenceCodeGeneration, Options.Abbr.Reference);
 
                helpCategory.Options[3] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.CollectionType, SR.Format(SR.ParametersCollectionType));
                helpCategory.Options[3].HelpText = SR.Format(SR.HelpCollectionType, Options.Abbr.CollectionType);
 
                helpCategory.Options[4] = ArgumentInfo.CreateParameterHelpInfo(Options.Cmd.ExcludeType, SR.Format(SR.ParametersExcludeType));
                helpCategory.Options[4].HelpText = SR.Format(SR.HelpExcludeTypeCodeGeneration, Options.Abbr.ExcludeType);
 
                helpCategory.Options[5] = ArgumentInfo.CreateFlagHelpInfo(Options.Cmd.Nostdlib);
                helpCategory.Options[5].HelpText = SR.Format(SR.HelpNostdlib);
 
                helpCategory.WriteHelp();
            }
 
            internal static void WriteExamples()
            {
                HelpCategory helpCategory = new HelpCategory(SR.Format(SR.HelpExamples));
                helpCategory.WriteHelp();
 
                WriteExample(SR.HelpExamples1, SR.HelpExamples2);
            }
 
            private static void WriteExample(string syntax, string explanation)
            {
                ToolConsole.WriteLine(string.Format(CultureInfo.InvariantCulture, " {0}", syntax));
                s_exampleBuilder.WriteParagraph(string.Format(CultureInfo.InvariantCulture, "    {0}", explanation));
                ToolConsole.WriteLine();
            }
 
            private class ArgumentInfo
            {
                private const string argHelpPrefix = " ";
                private const string argHelpSeperator = " - ";
 
                internal static ArgumentInfo CreateInputHelpInfo(string input)
                {
                    ArgumentInfo argInfo = new ArgumentInfo();
                    argInfo._name = input;
                    return argInfo;
                }
 
                internal static ArgumentInfo CreateFlagHelpInfo(string option)
                {
                    ArgumentInfo argInfo = new ArgumentInfo();
                    argInfo._name = $"--{option}";
                    return argInfo;
                }
 
                internal static ArgumentInfo CreateParameterHelpInfo(string option, string optionUse)
                {
                    ArgumentInfo argInfo = new ArgumentInfo();
                    argInfo._name = $"--{option}:{optionUse}";
                    return argInfo;
                }
 
                private bool _beginGroup;
                private string _name;
                private string _helpText;
 
                public bool BeginGroup
                {
                    get { return _beginGroup; }
                    set { _beginGroup = value; }
                }
 
                public string Name
                {
                    get { return _name; }
                }
                public string HelpText
                {
                    set { _helpText = value; }
                }
 
                private string GenerateHelp(string pattern)
                {
                    return string.Format(CultureInfo.InvariantCulture, pattern, _name, _helpText);
                }
 
                private static int CalculateMaxNameLength(ArgumentInfo[] arguments)
                {
                    int maxNameLength = 0;
                    foreach (ArgumentInfo argument in arguments)
                    {
                        if (argument.Name.Length > maxNameLength)
                        {
                            maxNameLength = argument.Name.Length;
                        }
                    }
                    return maxNameLength;
                }
 
                public static void WriteArguments(ArgumentInfo[] arguments)
                {
                    int maxArgumentLength = CalculateMaxNameLength(arguments);
                    int helpTextIndent = argHelpPrefix.Length + maxArgumentLength + argHelpSeperator.Length;
                    string helpPattern = argHelpPrefix + "{0, -" + maxArgumentLength + "}" + argHelpSeperator + "{1}";
 
                    ToolStringBuilder builder = new ToolStringBuilder(helpTextIndent);
 
                    foreach (ArgumentInfo argument in arguments)
                    {
                        if (argument.BeginGroup)
                            ToolConsole.WriteLine();
 
                        string optionHelp = argument.GenerateHelp(helpPattern);
                        builder.WriteParagraph(optionHelp);
                    }
                }
            }
 
            private class HelpCategory
            {
                static HelpCategory()
                {
                    try
                    {
#pragma warning disable CA1416 // Validate platform compatibility
                        bool junk = Console.CursorVisible;
#pragma warning restore CA1416 // Validate platform compatibility
                        if (Console.WindowWidth > 75)
                            s_nameMidpoint = Console.WindowWidth / 3;
                        else
                            s_nameMidpoint = 25;
                    }
                    catch
                    {
                        s_nameMidpoint = 25;
                    }
                }
 
                private static ToolStringBuilder s_categoryBuilder = new ToolStringBuilder(4);
                private static int s_nameMidpoint;
 
                private int _nameStart;
                private string _name;
                private string _description = null;
                private string _syntax = null;
                private ArgumentInfo[] _options;
                private ArgumentInfo[] _inputs;
 
                public HelpCategory(string name)
                {
                    Tool.Assert(name != null, "Name should never be null");
                    _name = name;
                    _nameStart = s_nameMidpoint - (name.Length / 2);
                }
 
                public string Description
                {
                    set { _description = value; }
                }
 
                public string Syntax
                {
                    set { _syntax = value; }
                }
 
                public ArgumentInfo[] Options
                {
                    get { return _options; }
                    set { _options = value; }
                }
 
                public ArgumentInfo[] Inputs
                {
                    get { return _inputs; }
                    set { _inputs = value; }
                }
 
                public void WriteHelp()
                {
                    int start = s_nameMidpoint;
                    ToolConsole.WriteLine(new string(' ', _nameStart) + _name);
                    ToolConsole.WriteLine();
 
                    if (_description != null)
                    {
                        s_categoryBuilder.WriteParagraph(_description);
                        ToolConsole.WriteLine();
                    }
 
                    if (_syntax != null)
                    {
                        s_categoryBuilder.WriteParagraph(_syntax);
                        ToolConsole.WriteLine();
                    }
                    if (_inputs != null)
                    {
                        ArgumentInfo.WriteArguments(_inputs);
                        ToolConsole.WriteLine();
                    }
 
                    if (_options != null)
                    {
                        ToolConsole.WriteLine(SR.Format(SR.HelpOptions));
                        ToolConsole.WriteLine();
                        ArgumentInfo.WriteArguments(_options);
                        ToolConsole.WriteLine();
                    }
                }
            }
        }
 
        private class ToolStringBuilder
        {
            private int _indentLength;
            private int _cursorLeft;
            private int _lineWidth;
            private StringBuilder _stringBuilder;
 
            public ToolStringBuilder(int indentLength)
            {
                _indentLength = indentLength;
            }
 
            private void Reset()
            {
                _stringBuilder = new StringBuilder();
                _cursorLeft = GetConsoleCursorLeft();
                _lineWidth = GetBufferWidth();
            }
 
            public void WriteParagraph(string text)
            {
                this.Reset();
                this.AppendParagraph(text);
                ToolConsole.WriteLine(_stringBuilder.ToString());
                _stringBuilder = null;
            }
 
            private void AppendParagraph(string text)
            {
                Tool.Assert(_stringBuilder != null, "stringBuilder cannot be null");
 
                int index = 0;
                while (index < text.Length)
                {
                    this.AppendWord(text, ref index);
                    this.AppendWhitespace(text, ref index);
                }
            }
 
            private void AppendWord(string text, ref int index)
            {
                // If we're at the beginning of a new line we should indent.
                if ((_cursorLeft == 0) && (index != 0))
                    AppendIndent();
 
                int wordLength = FindWordLength(text, index);
 
                // Now that we know how long the string is we can:
                //   1. print it on the current line if we have enough space
                //   2. print it on the next line if we don't have space 
                //      on the current line and it will fit on the next line
                //   3. print whatever will fit on the current line 
                //      and overflow to the next line.
                if (wordLength < this.HangingLineWidth)
                {
                    if (wordLength > this.BufferWidth)
                    {
                        this.AppendLineBreak();
                        this.AppendIndent();
                    }
                    _stringBuilder.Append(text, index, wordLength);
                    _cursorLeft += wordLength;
                }
                else
                {
                    AppendWithOverflow(text, ref index, ref wordLength);
                }
 
                index += wordLength;
            }
 
            private void AppendWithOverflow(string test, ref int start, ref int wordLength)
            {
                do
                {
                    _stringBuilder.Append(test, start, this.BufferWidth);
                    start += this.BufferWidth;
                    wordLength -= this.BufferWidth;
                    this.AppendLineBreak();
 
                    if (wordLength > 0)
                        this.AppendIndent();
                } while (wordLength > this.BufferWidth);
 
                if (wordLength > 0)
                {
                    _stringBuilder.Append(test, start, wordLength);
                    _cursorLeft += wordLength;
                }
            }
 
            private void AppendWhitespace(string text, ref int index)
            {
                while ((index < text.Length) && char.IsWhiteSpace(text[index]))
                {
                    if (BufferWidth == 0)
                    {
                        this.AppendLineBreak();
                    }
 
                    // For each whitespace character:
                    //   1. If we're at a newline character we insert 
                    //      a new line and reset the cursor.
                    //   2. If the whitespace character is at the beginning of a new 
                    //      line, we insert an indent instead of the whitespace
                    //   3. Insert the whitespace 
                    if (AtNewLine(text, index))
                    {
                        this.AppendLineBreak();
                        index += Environment.NewLine.Length;
                    }
                    else if (_cursorLeft == 0 && index != 0)
                    {
                        AppendIndent();
                        index++;
                    }
                    else
                    {
                        _stringBuilder.Append(text[index]);
                        index++;
                        _cursorLeft++;
                    }
                }
            }
 
            private void AppendIndent()
            {
                _stringBuilder.Append(' ', _indentLength);
                _cursorLeft += _indentLength;
            }
 
            private void AppendLineBreak()
            {
                if (BufferWidth != 0)
                    _stringBuilder.AppendLine();
                _cursorLeft = 0;
            }
 
            private int BufferWidth
            {
                get
                {
                    return _lineWidth - _cursorLeft;
                }
            }
 
            private int HangingLineWidth
            {
                get
                {
                    return _lineWidth - _indentLength;
                }
            }
 
            private static int FindWordLength(string text, int index)
            {
                for (int end = index; end < text.Length; end++)
                {
                    if (char.IsWhiteSpace(text[end]))
                        return end - index;
                }
                return text.Length - index;
            }
 
            private static bool AtNewLine(string text, int index)
            {
                if ((index + Environment.NewLine.Length) > text.Length)
                {
                    return false;
                }
 
                for (int i = 0; i < Environment.NewLine.Length; i++)
                {
                    if (Environment.NewLine[i] != text[index + i])
                    {
                        return false;
                    }
                }
 
                return true;
            }
 
            private static int GetConsoleCursorLeft()
            {
                try
                {
                    return Console.CursorLeft;
                }
                catch
                {
                    return 0;
                }
            }
 
            private static int GetBufferWidth()
            {
                try
                {
#pragma warning disable CA1416 // Validate platform compatibility
                    bool junk = Console.CursorVisible;
#pragma warning restore CA1416 // Validate platform compatibility
                    return Console.BufferWidth;
                }
                catch
                {
                    return int.MaxValue;
                }
            }
        }
    }
}