File: Microsoft\Tools\ServiceModel\SvcUtil\Options.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.Configuration;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
 
 
    internal partial class Options
    {
        private ToolMode? _defaultMode;
        private ToolMode _validModes = ToolMode.Any;
        private string _modeSettingOption;
        private string _modeSettingValue;
 
        private string _targetValue;
 
        private string _outputFileArg;
        private string _directoryArg;
        private string _configFileArg;
 
        private bool _noLogo;
        private bool _quiet;
        private List<string> _inputParameters;
        private List<Type> _referencedTypes;
        private List<Assembly> _referencedAssemblies;
        private Dictionary<string, Type> _excludedTypes;
        private bool _nostdlib;
        private Dictionary<string, string> _namespaceMappings;
 
        internal string OutputFileArg { get { return _outputFileArg; } }
        internal string DirectoryArg { get { return _directoryArg; } }
        internal bool NoLogo { get { return _noLogo; } }
        internal bool Quiet { get { return _quiet; } }
 
        internal List<string> InputParameters { get { return _inputParameters; } }
        internal List<Type> ReferencedTypes { get { return _referencedTypes; } }
        internal List<Assembly> ReferencedAssemblies { get { return _referencedAssemblies; } }
        internal bool Nostdlib { get { return _nostdlib; } }
        internal Dictionary<string, string> NamespaceMappings { get { return _namespaceMappings; } }
        private TypeResolver _typeResolver;
 
        internal string ModeSettingOption { get { return _modeSettingOption; } }
        internal string ModeSettingValue { get { return _modeSettingValue; } }
 
        private Options(ArgumentDictionary arguments)
        {
            OptionProcessingHelper optionProcessor = new OptionProcessingHelper(this, arguments);
            optionProcessor.ProcessArguments();
        }
 
        internal static Options ParseArguments(string[] args)
        {
            ArgumentDictionary arguments;
            try
            {
                arguments = CommandParser.ParseCommand(args, Options.Switches.All);
            }
            catch (ArgumentException ae)
            {
                throw new ToolOptionException(ae.Message);
            }
            return new Options(arguments);
        }
 
        internal void SetAllowedModes(ToolMode newDefaultMode, ToolMode validModes, string option, string value)
        {
            Tool.Assert(validModes != ToolMode.None, "validModes should never be set to None!");
            Tool.Assert(newDefaultMode != ToolMode.None, "newDefaultMode should never be set to None!");
            Tool.Assert((validModes & newDefaultMode) != ToolMode.None, "newDefaultMode must be a validMode!");
            Tool.Assert(IsSingleBit(newDefaultMode), "newDefaultMode must Always represent a single mode!");
 
            //update/filter list of valid modes
            _validModes &= validModes;
            if (_validModes == ToolMode.None)
                throw new InvalidToolModeException();
 
 
            bool currentDefaultIsValid = (_defaultMode.HasValue && (_defaultMode & _validModes) != ToolMode.None);
            bool newDefaultIsValid = (newDefaultMode & _validModes) != ToolMode.None;
            if (!currentDefaultIsValid)
            {
                if (newDefaultIsValid)
                    _defaultMode = newDefaultMode;
                else
                    _defaultMode = null;
            }
 
            //If this is true, then this is an explicit mode setting
            if (IsSingleBit(validModes))
            {
                _modeSettingOption = option;
                _modeSettingValue = value;
            }
        }
 
        internal ToolMode? GetToolMode()
        {
            if (IsSingleBit(_validModes))
                return _validModes;
            return _defaultMode;
        }
 
        internal string GetCommandLineString(string option, string value)
        {
            return (value == null) ? option : option + ":" + value;
        }
 
        private static bool IsSingleBit(ToolMode mode)
        {
            //figures out if the mode has a single bit set ( is a power of 2)
            int x = (int)mode;
            return (x != 0) && ((x & (x + ~0)) == 0);
        }
 
        internal bool IsTypeExcluded(Type type)
        {
            return OptionProcessingHelper.IsTypeSpecified(type, _excludedTypes, Options.Cmd.ExcludeType);
        }
 
        private class OptionProcessingHelper
        {
            private Options _parent;
            private ArgumentDictionary _arguments;
 
 
            private static Type s_typeOfDateTimeOffset = typeof(DateTimeOffset);
 
            internal OptionProcessingHelper(Options options, ArgumentDictionary arguments)
            {
                _parent = options;
                _arguments = arguments;
            }
 
            internal void ProcessArguments()
            {
                CheckForBasicOptions();
 
                if (CheckForHelpOption() || !CheckForQuietOption())
                    return;
 
                LoadSMReferenceAssembly();
                ProcessDirectoryOption();
                ProcessOutputOption();
                ReadInputArguments();
                ParseNamespaceMappings();
                ParseReferenceAssemblies();
            }
 
            private bool CheckForHelpOption()
            {
                if (_arguments.ContainsArgument(Options.Cmd.Help) || _arguments.Count == 0)
                {
                    _parent.SetAllowedModes(ToolMode.DisplayHelp, ToolMode.DisplayHelp, Options.Cmd.Help, null);
                    return true;
                }
                return false;
            }
 
            private bool CheckForQuietOption()
            {
                _parent._quiet = _arguments.ContainsArgument(Options.Cmd.Quiet);
                return _parent._quiet;
            }
 
            private void CheckForBasicOptions()
            {
                _parent._noLogo = _arguments.ContainsArgument(Options.Cmd.NoLogo);
#if DEBUG
                ToolConsole.SetOptions(_arguments.ContainsArgument(Options.Cmd.Debug));
#endif
            }
 
            private void ProcessDirectoryOption()
            {
                // Directory
                //---------------------------------------------------------------------------------------------------------
                if (_arguments.ContainsArgument(Options.Cmd.Directory))
                {
                    string directoryArgValue = _arguments.GetArgument(Options.Cmd.Directory);
 
                    try
                    {
                        ValidateIsDirectoryPathOnly(Options.Cmd.Directory, directoryArgValue);
 
                        if (!directoryArgValue.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
                            directoryArgValue += Path.DirectorySeparatorChar;
 
                        _parent._directoryArg = Path.GetFullPath(directoryArgValue);
                    }
                    catch (ToolOptionException)
                    {
                        throw;
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Tool.IsFatal(e))
                            throw;
 
                        throw new ToolArgumentException(SR.Format(SR.ErrInvalidPath, directoryArgValue, Options.Cmd.Directory), e);
                    }
                }
                else
                {
                    _parent._directoryArg = null;
                }
            }
 
            private static void ValidateIsDirectoryPathOnly(string arg, string value)
            {
                ValidatePath(arg, value);
                FileInfo fileInfo = new FileInfo(value);
                if (fileInfo.Exists)
                    throw new ToolOptionException(SR.Format(SR.ErrDirectoryPointsToAFile, arg, value));
            }
 
            private static void ValidatePath(string arg, string value)
            {
                int invalidCharacterIndex = value.IndexOfAny(Path.GetInvalidPathChars());
 
                if (invalidCharacterIndex != -1)
                {
                    string invalidCharacter = value[invalidCharacterIndex].ToString();
                    throw new ToolOptionException(SR.Format(SR.ErrDirectoryContainsInvalidCharacters, arg, value, invalidCharacter));
                }
            }
 
            private void ProcessOutputOption()
            {
                if (_arguments.ContainsArgument(Options.Cmd.Out))
                {
                    _parent._outputFileArg = _arguments.GetArgument(Options.Cmd.Out);
 
                    if (_parent._outputFileArg != string.Empty)
                    {
                        SetAllowedModesFromOption(ToolMode.XmlSerializerGeneration, ToolMode.XmlSerializerGeneration, Options.Cmd.Out, "");
 
                        ValidatePath(Options.Cmd.Out, _parent._outputFileArg);
                    }
                }
                else
                {
                    _parent._outputFileArg = null;
                }
            }
 
            private void ReadInputArguments()
            {
                _parent._inputParameters = new List<string>(_arguments.GetArguments(String.Empty));
            }
 
            private void ParseNamespaceMappings()
            {
                IList<string> namespaceMappingsArgs = _arguments.GetArguments(Options.Cmd.Namespace);
                _parent._namespaceMappings = new Dictionary<string, string>(namespaceMappingsArgs.Count);
 
                foreach (string namespaceMapping in namespaceMappingsArgs)
                {
                    string[] parts = namespaceMapping.Split(',');
 
                    if (parts == null || parts.Length != 2)
                        throw new ToolOptionException(SR.Format(SR.ErrInvalidNamespaceArgument, Options.Cmd.Namespace, namespaceMapping));
 
                    string targetNamespace = parts[0].Trim();
                    string clrNamespace = parts[1].Trim();
 
                    if (_parent._namespaceMappings.ContainsKey(targetNamespace))
                    {
                        string prevClrNamespace = _parent._namespaceMappings[targetNamespace];
                        if (prevClrNamespace != clrNamespace)
                            throw new ToolOptionException(SR.Format(SR.ErrCannotSpecifyMultipleMappingsForNamespace,
                                Options.Cmd.Namespace, targetNamespace, prevClrNamespace, clrNamespace));
                    }
                    else
                    {
                        _parent._namespaceMappings.Add(targetNamespace, clrNamespace);
                    }
                }
            }
 
            private void ParseReferenceAssemblies()
            {
                IList<string> referencedAssembliesArgs = _arguments.GetArguments(Options.Cmd.Reference);
                IList<string> excludeTypesArgs = _arguments.GetArguments(Options.Cmd.ExcludeType);
                IList<string> referencedCollectionTypesArgs = _arguments.GetArguments(Options.Cmd.CollectionType);
                bool nostdlib = _arguments.ContainsArgument(Options.Cmd.Nostdlib);
 
                if (excludeTypesArgs != null && excludeTypesArgs.Count > 0)
                    SetAllowedModesFromOption(ToolMode.XmlSerializerGeneration, ToolMode.XmlSerializerGeneration, Options.Cmd.ExcludeType, null);
 
                AddReferencedTypes(referencedAssembliesArgs, excludeTypesArgs, referencedCollectionTypesArgs, nostdlib);
                _parent._typeResolver = CreateTypeResolver(_parent);
            }
 
            private void SetAllowedModesFromOption(ToolMode newDefaultMode, ToolMode allowedModes, string option, string value)
            {
                try
                {
                    _parent.SetAllowedModes(newDefaultMode, allowedModes, option, value);
                }
                catch (InvalidToolModeException)
                {
                    string optionStr = _parent.GetCommandLineString(option, value);
                    if (_parent._modeSettingOption != null)
                    {
                        if (_parent._modeSettingOption == Options.Cmd.Target)
                        {
                            throw new ToolOptionException(SR.Format(SR.ErrOptionConflictsWithTarget, Options.Cmd.Target, _parent.ModeSettingValue, optionStr));
                        }
                        else
                        {
                            string modeSettingStr = _parent.GetCommandLineString(_parent._modeSettingOption, _parent._modeSettingValue);
                            throw new ToolOptionException(SR.Format(SR.ErrOptionModeConflict, optionStr, modeSettingStr));
                        }
                    }
                    else
                    {
                        throw new ToolOptionException(SR.Format(SR.ErrAmbiguousOptionModeConflict, optionStr));
                    }
                }
            }
 
            private void AddReferencedTypes(IList<string> referenceArgs, IList<string> excludedTypeArgs, IList<string> collectionTypesArgs, bool nostdlib)
            {
                _parent._referencedTypes = new List<Type>();
                _parent._referencedAssemblies = new List<Assembly>(referenceArgs.Count);
                _parent._nostdlib = nostdlib;
                _parent._excludedTypes = AddSpecifiedTypesToDictionary(excludedTypeArgs, Options.Cmd.ExcludeType);
 
 
                Dictionary<string, Type> foundCollectionTypes = AddSpecifiedTypesToDictionary(collectionTypesArgs, Options.Cmd.CollectionType);
 
                LoadReferencedAssemblies(referenceArgs);
 
                foreach (Assembly assembly in _parent._referencedAssemblies)
                {
                    AddReferencedTypesFromAssembly(assembly, foundCollectionTypes);
                }
 
                if (!nostdlib)
                {
                    AddMscorlib(foundCollectionTypes);
                }
            }
 
            private void LoadReferencedAssemblies(IList<string> referenceArgs)
            {
                foreach (string path in referenceArgs)
                {
                    Assembly assembly;
                    try
                    {
                        assembly = InputModule.LoadAssembly(path);
                        if (!_parent._referencedAssemblies.Contains(assembly))
                        {
                            _parent._referencedAssemblies.Add(assembly);
                        }
                        else
                        {
                            throw new ToolOptionException(SR.Format(SR.ErrDuplicateReferenceValues, Options.Cmd.Reference, assembly.Location));
                        }
                    }
#pragma warning suppress 56500 // covered by FxCOP
                    catch (Exception e)
                    {
                        if (Tool.IsFatal(e))
                            throw;
 
                        ToolConsole.WriteWarning(SR.Format(SR.ErrCouldNotLoadReferenceAssemblyAt, path));
                    }
                }
            }
 
            private Dictionary<string, Type> AddSpecifiedTypesToDictionary(IList<string> typeArgs, string cmd)
            {
                Dictionary<string, Type> specifiedTypes = new Dictionary<string, Type>(typeArgs.Count);
                foreach (string typeArg in typeArgs)
                {
                    if (specifiedTypes.ContainsKey(typeArg))
                        throw new ToolOptionException(SR.Format(SR.ErrDuplicateValuePassedToTypeArg, cmd, typeArg));
                    specifiedTypes.Add(typeArg, null);
                }
                return specifiedTypes;
            }
 
            private void AddReferencedTypesFromAssembly(Assembly assembly, Dictionary<string, Type> foundCollectionTypes)
            {
                foreach (Type type in InputModule.LoadTypes(assembly))
                {
                    if (type.IsPublic || type.IsNestedPublic)
                    {
                        if (!_parent.IsTypeExcluded(type))
                            _parent._referencedTypes.Add(type);
                    }
                }
            }
 
            private void AddMscorlib(Dictionary<string, Type> foundCollectionTypes)
            {
                Assembly mscorlib = typeof(int).Assembly;
                if (!_parent._referencedAssemblies.Contains(mscorlib))
                {
                    AddReferencedTypesFromAssembly(mscorlib, foundCollectionTypes);
                }
            }
 
            private void LoadSMReferenceAssembly()
            {
                string smReferenceArg = _arguments.GetArgument(Options.Cmd.SMReference);
                IList<string> referencedAssembliesArgs = smReferenceArg.Split(';').ToList();
                if (referencedAssembliesArgs != null && referencedAssembliesArgs.Count > 0)
                {
                    string smassembly = "";
                    string smpassembly = "";
                    foreach (string path in referencedAssembliesArgs)
                    {
                        var file = new FileInfo(path);
 
                        if (file.Name.Equals("System.ServiceModel.Primitives.dll", StringComparison.OrdinalIgnoreCase))
                        {
                            smassembly = path;
                        }
 
                        if (file.Name.Equals("System.Private.ServiceModel.dll", StringComparison.OrdinalIgnoreCase))
                        {
                            smpassembly = path;
                        }
                    }
                    if (string.IsNullOrEmpty(smassembly))
                    {
                        ToolConsole.WriteError("Missing System.ServiceModel.Primitives");
                        throw new ArgumentException("Invalid smreference value");
                    }
 
                    try
                    {
                        ToolConsole.WriteLine("Load Assembly From " + smassembly);
                        Tool.SMAssembly = InputModule.LoadAssembly(smassembly);
                        ToolConsole.WriteLine($"Successfully Load {smassembly}");
                    }
                    catch (Exception e)
                    {
                        ToolConsole.WriteError(string.Format("Fail to load the assembly {0} with the error {1}", smassembly, e.Message));
                        throw;
                    }
 
                    if (!string.IsNullOrEmpty(smpassembly))
                    {
                        try
                        {
                            ToolConsole.WriteLine("Load Assembly From " + smpassembly);
                            Tool.SMAssembly = InputModule.LoadAssembly(smpassembly);
                            ToolConsole.WriteLine($"Successfully Load {smpassembly}");
                        }
                        catch (Exception e)
                        {
                            ToolConsole.WriteError(string.Format("Fail to load the assembly {0} with the error {1}", smpassembly, e.Message));
                            throw;
                        }
                    }
                }
                else
                {
                    ToolConsole.WriteError("Need to pass the System.ServiceModel.Primitives.dll path through the 'smreference' parameter.");
                    throw new ArgumentException("Need to pass the System.ServiceModel.Primitives.dll path through the 'smreference' parameter.");
                }
            }
 
            internal static bool IsTypeSpecified(Type type, Dictionary<string, Type> specifiedTypes, string cmd)
            {
                Type foundType = null;
                string foundTypeName = null;
 
                // Search the Dictionary for the type
                // --------------------------------------------------------------------------------------------------------
                if (specifiedTypes.TryGetValue(type.FullName, out foundType))
                    foundTypeName = type.FullName;
 
                if (specifiedTypes.TryGetValue(type.AssemblyQualifiedName, out foundType))
                    foundTypeName = type.AssemblyQualifiedName;
 
                // Throw appropriate error message if we found something and the entry value wasn't null
                // --------------------------------------------------------------------------------------------------------
                if (foundTypeName != null)
                {
                    if (foundType != null && foundType != type)
                    {
                        throw new ToolOptionException(SR.Format(SR.ErrCannotDisambiguateSpecifiedTypes,
                            cmd, type.AssemblyQualifiedName, foundType.AssemblyQualifiedName));
                    }
                    else
                    {
                        specifiedTypes[foundTypeName] = type;
                    }
                    return true;
                }
 
                return false;
            }
 
            static TypeResolver CreateTypeResolver(Options options)
            {
                TypeResolver typeResolver = new TypeResolver(options);
                AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(typeResolver.ResolveType);
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(typeResolver.ResolveAssembly);
 
                return typeResolver;
            }
        }
    }
}