Click here to Skip to main content
15,895,370 members
Articles / .NET

Versioning Controlled Build

Rate me:
Please Sign up or sign in to vote.
4.90/5 (237 votes)
8 Dec 2013CPOL35 min read 2.2M   20.6K   779  
A Visual Studio add-in and command-line utility that automates versioning of .NET and VC++ projects
/*
 * Filename:    CommandLineArgs.cs
 * Product:     Versioning Controlled Build
 * Solution:    BuildAutoIncrement
 * Project:     CommandLine
 * Description: Class that parses command-line arguments.
 * Copyright:   Julijan �ribar, 2004-2013
 * 
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author(s) be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Resources;
using System.Text;

namespace BuildAutoIncrement {

    /// <summary>
	///   Class representing arguments passed to command line tool.
	/// </summary>
	public sealed class CommandLineArgs {

        private class DuplicateArgumentException : ArgumentException {
            public DuplicateArgumentException(string message) : base(message) {
            }
        }

        /// <summary>
        ///   Enumeration for project synchronization.
        /// </summary>
        public enum ProjectsSynchronization {
            NotDefined,
            Synchronize,
            IncrementAndSynchronize,
            None
        }

        #region ProjectsNameList

        /// <summary>
        ///   A list of strings used for case insensitive project names 
        ///   comparison.
        /// </summary>
        private class ProjectsNameList : ArrayList {

            /// <summary>
            ///   Overrides <c>Contains</c> method to do case insensitive check.
            /// </summary>
            /// <param name="name">
            ///   String to check.
            /// </param>
            /// <returns>
            ///   <c>true</c> if string is found in the list, otherwise <c>false</c>.
            /// </returns>
            public override bool Contains(object name) {
                Debug.Assert(name is string);
                foreach (string item in this) {
                    if (string.Compare((string)name, item, true) == 0)
                        return true;
                }
                return false;
            }
            
        }

        #endregion // ProjectsNameList
        
        #region CommandLineSwitchesTable

        /// <summary>
        ///   Enumeration representing command-line switches.
        /// </summary>
        enum CommandLineSwitch {
            Empty,
            Gui,
            Apply,
            Projects,
            Synchronize,
            VersionTypes,
            DisplaySummary,
            SourceSafeUser,
            CheckProjectsExistance
        }

        /// <summary>
        ///   Table with all possible command-line switches.
        /// </summary>
        private class CommandLineSwitchesTable : Hashtable {

            /// <summary>
            ///   Initializes <c>CommandLineSwitchesTable</c> object.
            /// </summary>
            // NOTE: all switches must be in lower case!
            public CommandLineSwitchesTable() {
                this[CommandLineSwitch.Gui]                     = "/g";     // Start GUI
                this[CommandLineSwitch.Projects]                = "/p";     // Projects for which versions may be modified:
                //    projects   comma separated list of projects to which
                //               modifications are restricted
                this[CommandLineSwitch.Apply]                   = "/a";     // Apply versions to:
                                                                            //    A     all projects
                                                                            //    M     modified projects only (default)
                this[CommandLineSwitch.VersionTypes]            = "/v";     // Version types to modify:
                                                                            //    *     all version types (see below)
                                                                            //    A     Assembly version
                                                                            //    P     Product (Informational) version
                                                                            //    F     File version
                this[CommandLineSwitch.Synchronize]             = "/s";     // Synchronize versions:
                                                                            //    S     Synchronize
                                                                            //    I     Increment and then synchronize
                                                                            //    N     No synchronization
                this[CommandLineSwitch.DisplaySummary]          = "/m";     // Display summary:
                                                                            //    C     console (default)
                                                                            //    F     file (with optional filename may be provided)
                                                                            //    N     none
                this[CommandLineSwitch.SourceSafeUser]          = "/u";     // Provide username and/or password. If nothing provided, this 
                                                                            // causes username/password prompt
                this[CommandLineSwitch.CheckProjectsExistance]  = "/c";     // Check if projects provided by some switches exist:
                                                                            //    E     check files (default). In case of error, command is 
                                                                            //          not executed
                                                                            //    W     do not check; only warning is issued.
            }

            /// <summary>
            ///   Outputs brief description of command-line utility to console.
            /// </summary>
            /// <param name="executableName">
            ///   Utility executable name.
            /// </param>
            public static void OutputDescription(string executableName) {
                Console.WriteLine(string.Format("{0} /G [solution]", executableName));
                Console.WriteLine(string.Format("{0} [solution [version]] [/P:projects] [/C:(E|W)] [/A:(A|M)]", executableName));
                Console.WriteLine("        [/V:(*|[A][F][P|I])] [/S:(S|I|N)] [/M:([C][F[\"filename\"]]|N)]");
                Console.WriteLine("        [/U[:(username[,password])]]");
                Console.WriteLine();
                Console.WriteLine("  solution   Solution file (.SLN or .DSW) for which versions should be updated.");
                
                Console.WriteLine("  /G         Starts GUI form with a list of all projects.");

                Console.WriteLine("  version    Version pattern to be applied, e.g. \"1.2.*.+2\".");

                Console.WriteLine("  /P         Specification for individual projects.");
                
                Console.WriteLine("  projects   List of comma separated project names prefixed by:");
                Console.WriteLine("               +       (optional) include into the list of projects for which");
                Console.WriteLine("                       version may be modified;");
                Console.WriteLine("               -       include into the list of projects for which version must");
                Console.WriteLine("                       not be modified;");
                Console.WriteLine("               !       include into the list of projects for which version is");
                Console.WriteLine("                       allways modified.");
                
                Console.WriteLine("  /C         Checks if project names provided with /P switch exist in solution.");
                Console.WriteLine("             Any invalid name causes:");
                Console.WriteLine("               E       Error - command is not executed (default);");
                Console.WriteLine("               W       Warning - command is executed ignoring invalid entries.");
                
                Console.WriteLine("  /A         Applies version to:");
                Console.WriteLine("               A       All projects;");
                Console.WriteLine("               M       Modified projects only (default).");
                
                Console.WriteLine("  /V         Version types to modify:");
                Console.WriteLine("               A       Assembly version;");
                Console.WriteLine("               P or I  Product (Informational) version;");
                Console.WriteLine("               F       File version;");
                Console.WriteLine("               *       All version types.");
                
                Console.WriteLine("  /S         Synchronization of versions for projects:");
                Console.WriteLine("               S       Synchronize (to the highest version);");
                Console.WriteLine("               I       Increment and then synchronize;");
                Console.WriteLine("               N       Increment versions independently.");
                
                Console.WriteLine("  /M         Version update summary:");
                Console.WriteLine("               C       Output to console (default);");
                Console.WriteLine("               F       Write to a file (with optional filename);");
                Console.WriteLine("               N       Suppress the summary.");
                
                Console.WriteLine("  /U         SourceSafe username and password.");
                
                Console.WriteLine();
            }

            /// <summary>
            ///   Hides base class <c>ContainsValue</c> method to ensure type
            ///   safety.
            /// </summary>
            /// <param name="value">
            ///   Value to check.
            /// </param>
            /// <returns>
            ///   <c>true</c> if value exists, otherwise <c>false</c>.
            /// </returns>
            private new bool ContainsValue(object value) {
                Debug.Assert(value is string);
                return ContainsValue((string)value);
            }

            /// <summary>
            ///   Checks if a switch exists in the table. Comparison is case
            ///   insensitive.
            /// </summary>
            /// <param name="value">
            ///   Value to check.
            /// </param>
            /// <returns>
            ///   <c>true</c> if value exists, otherwise <c>false</c>.
            /// </returns>
            public bool ContainsValue(string value) {
                return base.ContainsValue(value.ToLower());
            }

            /// <summary>
            ///   Hides base class <c>ContainsKey</c> method to ensure type
            ///   safety.
            /// </summary>
            /// <param name="key">
            ///   Key to check.
            /// </param>
            /// <returns>
            ///   <c>true</c> if key exists, otherwise <c>false</c>.
            /// </returns>
            private new bool ContainsKey(object key) {
                Debug.Assert(key is CommandLineSwitch);
                return ContainsKey((CommandLineSwitch)key);
            }

            /// <summary>
            ///   Checks if a key exists in the table.
            /// </summary>
            /// <param name="key">
            ///   Key to check.
            /// </param>
            /// <returns>
            ///   <c>true</c> if key exists, otherwise <c>false</c>.
            /// </returns>
            public bool ContainsKey(CommandLineSwitch key) {
                return base.ContainsKey(key);
            }

            /// <summary>
            ///   Overides base class indexer to assure type-safe access.
            /// </summary>
            private new object this[object key] {
                get {
                    Debug.Assert(key is CommandLineSwitch);
                    return this[(CommandLineSwitch)key];
                }
                set {
                    Debug.Assert(key is CommandLineSwitch);
                    Debug.Assert(value is string);
                    this[(CommandLineSwitch)key] = (string)value;
                }
            }

            /// <summary>
            ///   Type-safe indexer to access value by key.
            /// </summary>
            public string this[CommandLineSwitch key] {
                get {
                    return (string)base[key];
                }
                set {
                    base[key] = value;
                }
            }

            /// <summary>
            ///   Gets a key for a string provided.
            /// </summary>
            /// <param name="switchString">
            ///   Switch string for which <c>CommandLineSwitch</c> is requested.
            ///   Search is case insesitive.
            /// </param>
            /// <returns>
            ///   <c>CommandLineSwitch</c> that corresponds to switch string 
            ///   provided or <c>CommandLineSwitch.Empty</c> if switch is not
            ///   found.
            /// </returns>
            public CommandLineSwitch GetKey(string switchString) {
                string sw = switchString.ToLower();
                Debug.Assert(ContainsValue(switchString));
                IDictionaryEnumerator en = base.GetEnumerator();
                while (en.MoveNext()) {
                    Debug.Assert(en.Value is string);
                    Debug.Assert(en.Key is CommandLineSwitch);
                    if ((string)en.Value == sw)
                        return (CommandLineSwitch)en.Key;
                }
                Debug.Assert(false, string.Format("Unknown command-line switch {0}", switchString));
                return CommandLineSwitch.Empty;
            }
        }

        #endregion // CommandLineSwitchesTable

        #region Constructors 

        /// <summary>
        ///   Initializes <c>CommandLineArgs</c> object.
        /// </summary>
        private CommandLineArgs() {
            CommandLineSwitches = new CommandLineSwitchesTable();
            m_solutionFilename = string.Empty;
            m_newVersion = string.Empty;
            m_applyToTypes = AssemblyVersionType.None;
            m_projectsAllowedForVersionModification = new ProjectsNameList();
            m_projectsToExclude = new ProjectsNameList();
            m_projectsToForce = new ProjectsNameList();
            m_displaySummary = new DisplaySummaryOptions();
            m_sourceSafeUserOptions = new SourceSafeUserOptions();
            m_checkProjectsExistance = true;
            m_projectsSynchronization = ProjectsSynchronization.NotDefined;
        }
		
        public CommandLineArgs(string[] args) : this() {
            ParseArguments(args);
		}

        #endregion // Constructors

        #region Public properties

        /// <summary>
        ///   Gets solution filename if provided by command-line arguments
        ///   or an empty string.
        /// </summary>
        public string SolutionFilename {
            get {
                return m_solutionFilename; 
            }
        }

        /// <summary>
        ///   Gets new version if provided by command-line arguments or an 
        ///   empty string.
        /// </summary>
        public string NewVersion {
            get {
                return m_newVersion; 
            }
        }

        /// <summary>
        ///   Returns <c>true</c> if start GUI switch is set.
        /// </summary>
        public bool StartGui {
            get {
                return m_startGui; 
            }
        }

        /// <summary>
        ///   Returns <c>true</c> if versioning should be applied to all 
        ///   projects or <c>false</c> if versioning should be applied to 
        ///   modified projects only.
        /// </summary>
        public bool ApplyToAllProjects {
            get {
                return m_applyToAllProjects;
            }
        }

        /// <summary>
        ///   Returns combination of <c>AssemblyVersionType</c> flags defining 
        ///   on which version types (AssemblyInfo, AssemblyFile or/and
        ///   AssemblyInformational) should version be applied.
        /// </summary>
        public AssemblyVersionType ApplyToTypes {
            get {
                return m_applyToTypes;
            }
        }

        /// <summary>
        ///   Gets <c>ProjectsSynchronization</c> enumeration that determines 
        ///   how projects are synchronized.
        /// </summary>
        public ProjectsSynchronization SynchronizeProjects {
            get {
                return m_projectsSynchronization;
            }
        }

        /// <summary>
        ///   Gets an array of project names allowed for versioning.
        /// </summary>
        public string[] ProjectsAllowedForVersionModification {
            get {
                return (string[])m_projectsAllowedForVersionModification.ToArray(typeof(string));
            }
        }

        /// <summary>
        ///   Gets an array of project names not allowed for versioning.
        /// </summary>
        public string[] ProjectsToExclude {
            get {
                return (string[])m_projectsToExclude.ToArray(typeof(string));
            }
        }

        /// <summary>
        ///   Gets an array of project names that must be versioned.
        /// </summary>
        public string[] ProjectsToForce {
            get {
                return (string[])m_projectsToForce.ToArray(typeof(string));
            }
        }

        /// <summary>
        ///   Gets <c>DisplaySummaryOptions</c> object that determines how
        ///   version update summary is displayed.
        /// </summary>
        public DisplaySummaryOptions DisplaySummaryOptions {
            get {
                return m_displaySummary;
            }
        }

        /// <summary>
        ///   Gets <c>SourceSafeUserOptions</c> with SourceSafe username and
        ///   password.
        /// </summary>
        public SourceSafeUserOptions SourceSafeUserOptions {
            get {
                return m_sourceSafeUserOptions;
            }
        }

        /// <summary>
        ///   Returns <c>true</c> if existance of individual projects provided
        ///   in project list must be checked, otherwise <c>false</c>.
        /// </summary>
        public bool CheckProjectsExistance {
            get {
                return m_checkProjectsExistance;
            }
        }

        #endregion Public properties

        #region Private methods

        /// <summary>
        ///   parses arfuments provided to commad line.
        /// </summary>
        /// <param name="args">
        ///   An array of strings.
        /// </param>
        private void ParseArguments(string[] args) {
            Debug.Assert(args != null);
            ArrayList switches = new ArrayList();
            foreach (string arg in args) {
                if (arg.StartsWith("/")) {
                    string sw = arg.Substring(0, 2);
                    if (!CommandLineSwitches.ContainsValue(sw)) 
                        throw new InvalidCommandLineArgumentException(string.Format(UnknownSwitch, arg));
                    CommandLineSwitch cls = CommandLineSwitches.GetKey(sw);
                    if (switches.IndexOf(cls) != -1)
                        throw new InvalidCommandLineArgumentException(string.Format(DuplicateSwitch, sw));
                    switches.Add(cls);
                    ProcessCommandLineSwitch(arg);
                }
                else {
                    ProcessNonSwitchArgument(arg);
                }
            }
        }

        /// <summary>
        ///   Parses command-line arguments that do not start with '/' 
        ///   character, i.e. solution name and version string.
        /// </summary>
        /// <param name="arg">
        ///   All arguments passed to command line.
        /// </param>
        private void ProcessNonSwitchArgument(string arg) {
            // first non-switch argument should be the solution filename, 
            // followed by optional version string
            if (m_solutionFilename.Length == 0) {
                if (Path.IsPathRooted(arg))
                    m_solutionFilename = arg;
                else
                    m_solutionFilename = Path.Combine(Environment.CurrentDirectory, arg);
                if (!File.Exists(m_solutionFilename))
                    throw new FileNotFoundException(string.Format(FileNotFound, m_solutionFilename), m_solutionFilename);
            }
            else if (m_newVersion.Length == 0) {
                m_newVersion = arg.Trim(new char[] { '\"', '\'' });
            }
            else {
                throw new InvalidCommandLineArgumentException(string.Format(TooManyArguments, arg));
            }
        }

        /// <summary>
        ///   Parses command-line switches, i.e. arguments that start with '/'
        ///   character.
        /// </summary>
        /// <param name="commandLineArg">
        ///   All arguments passed to command line.
        /// </param>
        private void ProcessCommandLineSwitch(string commandLineArg) {
            string[] splitSwitch = commandLineArg.Split(':');
            if (splitSwitch.Length > 2) {
                int n = commandLineArg.IndexOf(':') + 1;
                throw new InvalidCommandLineArgumentException(string.Format(InvalidSwitchFormat, splitSwitch[0], commandLineArg.Substring(n)));
            }
            try {
                switch (CommandLineSwitches.GetKey(splitSwitch[0])) {
                case CommandLineSwitch.Gui:
                    if (splitSwitch.Length > 1) {
                        int n = commandLineArg.IndexOf(':') + 1;
                        throw new InvalidCommandLineArgumentException(string.Format(InvalidSwitchFormat, splitSwitch[0], commandLineArg.Substring(n)));
                    }
                    m_startGui = true;
                    return;
                case CommandLineSwitch.Apply:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    ParseApplySwitch(splitSwitch[1]);
                    return;
                case CommandLineSwitch.Synchronize:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    ParseSynchronizationSwitch(splitSwitch[1]);
                    return;
                case CommandLineSwitch.Projects:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    ParseProjectsList(splitSwitch[1]);
                    return;
                case CommandLineSwitch.VersionTypes:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    ParseVersionSwitch(splitSwitch[1]);
                    return;
                case CommandLineSwitch.DisplaySummary:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    m_displaySummary = ParseDisplaySummaryOptions(splitSwitch[1]);
                    return;
                case CommandLineSwitch.SourceSafeUser:
                    if (splitSwitch.Length > 1 && splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();;
                    // if only '/u' provided
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        m_sourceSafeUserOptions = new SourceSafeUserOptions(true);
                    else
                        m_sourceSafeUserOptions = ParseSourceSafeOptions(splitSwitch[1]);
                    return;
                case CommandLineSwitch.CheckProjectsExistance:
                    if (splitSwitch.Length == 1 || splitSwitch[1].Length == 0)
                        throw new ArgumentNullException();
                    m_checkProjectsExistance = ParseCheckProjectsExistance(splitSwitch[1]);
                    return;
                default:
                    throw new InvalidCommandLineArgumentException(string.Format(UnknownSwitch, splitSwitch[0]));
                }
            }
            catch (FormatException exception) {
                throw new InvalidCommandLineArgumentException(string.Format(InvalidSwitchFormat, splitSwitch[0], exception.Message));
            }
            catch (DuplicateArgumentException exception) {
                throw new InvalidCommandLineArgumentException(string.Format(ErrorInSwitch, splitSwitch[0], exception.Message));
            }
            catch (ArgumentNullException) {
                throw new InvalidCommandLineArgumentException(string.Format(SwitchHasNoValue, splitSwitch[0]));
            }
            catch (ArgumentException exception) {
                throw new InvalidCommandLineArgumentException(string.Format(InvalidSwitchValue, splitSwitch[0], exception.Message));
            }
        }

        /// <summary>
        ///   Parses switch that defines to which projects version should be 
        ///   applied.
        /// </summary>
        /// <param name="switchValue">
        ///   Value provided with the switch.
        /// </param>
        private void ParseApplySwitch(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            switch (switchValue) {
            case "a":
            case "A":
                m_applyToAllProjects = true;
                break;
            case "m":
            case "M":
                break;
            default:
                throw new ArgumentException(switchValue);
            }
        }

        /// <summary>
        ///   Parses switch that defines to which version types versioning 
        ///   should be applied.
        /// </summary>
        /// <param name="switchValue">
        ///   Switch value to be parsed.
        /// </param>
        private void ParseVersionSwitch(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            if (switchValue == "*") {
                m_applyToTypes = AssemblyVersionType.All;
                return;
            }
            for (int i = 0; i < switchValue.Length; i++) {
                char ch = switchValue[i];
                switch (ch) {
                case 'a':
                case 'A':
                    SetVersionToApplyTo(AssemblyVersionType.AssemblyVersion);
                    break;
                case 'p':
                case 'P':
                case 'i':
                case 'I':
                    SetVersionToApplyTo(AssemblyVersionType.AssemblyInformationalVersion);
                    break;
                case 'f':
                case 'F':
                    SetVersionToApplyTo(AssemblyVersionType.AssemblyFileVersion);
                    break;
                case '*':
                    throw new FormatException(AsteriskValueMustbeAlone);
                default:
                    throw new ArgumentException(string.Format(InvalidSwitchValue, ch));
                }
            }
        }

        /// <summary>
        ///   Parses list provided with projects list.
        /// </summary>
        /// <param name="switchValue">
        ///   Value to parse
        /// </param>
        private void ParseProjectsList(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            foreach (string s in switchValue.Split(',')) {
                string projectName = null;
                switch (s[0]) {
                case '-':
                    projectName = ExtractProjectName(s.Substring(1));
                    CheckProjectNameAlreadyExists(projectName);
                    m_projectsToExclude.Add(projectName);
                    break;
                case '!':
                    projectName = ExtractProjectName(s.Substring(1));
                    CheckProjectNameAlreadyExists(projectName);
                    m_projectsToForce.Add(projectName);
                    break;
                case '+':
                    projectName = ExtractProjectName(s.Substring(1));
                    CheckProjectNameAlreadyExists(projectName);
                    m_projectsAllowedForVersionModification.Add(projectName);
                    break;
                default:
                    projectName = ExtractProjectName(s);
                    CheckProjectNameAlreadyExists(projectName);
                    m_projectsAllowedForVersionModification.Add(projectName);
                    break;
                }
            }
        }

        /// <summary>
        ///   Extracts project name, trimming optional quotes. In case of 
        ///   invalid format, <c>FormatException</c> is thrown.
        /// </summary>
        /// <param name="toExtract">
        ///   String to trim.
        /// </param>
        /// <returns>
        ///   Project name.
        /// </returns>
        private string ExtractProjectName(string toExtract) {
            if (toExtract.StartsWith("\"") != toExtract.EndsWith("\""))
                throw new FormatException(string.Format(NotEnclosedByQuotes, toExtract.Trim('\"')));
            return toExtract.Trim('\"');
        }

        /// <summary>
        ///   Checks if project name provided already exists in one of internal
        ///   lists. If exists, <c>ArgumentException</c> is thrown.
        /// </summary>
        /// <param name="name">
        ///   Name to check.
        /// </param>
        private void CheckProjectNameAlreadyExists(string name) {
            if (m_projectsAllowedForVersionModification.Contains(name) || m_projectsToExclude.Contains(name) || m_projectsToForce.Contains(name)) {
                throw new DuplicateArgumentException(string.Format(ProjectNameAlreadySpecified, name));
            }
        }

        /// <summary>
        ///   Sets version type flags to apply.
        /// </summary>
        /// <param name="assemblyVersionType">
        ///   <c>AssemblyVersionType</c> to apply.
        /// </param>
        private void SetVersionToApplyTo(AssemblyVersionType assemblyVersionType) {
            if ((m_applyToTypes & assemblyVersionType) == assemblyVersionType)
                throw new FormatException(string.Format(VersionFlagAlreadySet, assemblyVersionType.ToString()));
            m_applyToTypes |= assemblyVersionType;
        }

        /// <summary>
        ///   Parses Synchronization switch. Allowed forms (all preceeded by '/s:'):
        ///     S   synchronize
        ///     I   increment and then synchronize
        ///     N   increment indepentantly
        /// </summary>
        /// <param name="switchValue">
        /// </param>
        private void ParseSynchronizationSwitch(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            switch (switchValue) {
            case "i":
            case "I":
                m_projectsSynchronization = ProjectsSynchronization.IncrementAndSynchronize;
                return;
            case "s":
            case "S":
                m_projectsSynchronization = ProjectsSynchronization.Synchronize;
                return;
            case "n":
            case "N":
                m_projectsSynchronization = ProjectsSynchronization.None;
                return;
            default:
                throw new ArgumentException(switchValue);
            }
        }

        /// <summary>
        ///   Parses summary display options. Allowed forms (all preceeded by '/m':'):
        ///     C               - only to console (default)
        ///     F["filename"]   - to file, with optional filename
        ///     CF["filename"]
        ///     F["filename"]C
        ///     N               - suppress 
        /// </summary>
        /// <param name="sw">
        ///   
        /// </param>
        /// <param name="text">
        ///   One of above forms.
        /// </param>
        /// <returns></returns>
        private DisplaySummaryOptions ParseDisplaySummaryOptions(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            if (switchValue.ToLower().StartsWith("n")) {
                if (switchValue.Length > 1)
                    throw new ArgumentException(switchValue);
                return new DisplaySummaryOptions(false);
            }
            bool toConsole = false;
            bool toFile = false;
            string filename = string.Empty;
            StringBuilder sb = new StringBuilder(switchValue);
            int startOfFilename = switchValue.IndexOf("\"");
            if (startOfFilename > -1) {
                int endOfFilename = switchValue.IndexOf("\"", startOfFilename + 1);
                // must not be two consequetive enclosing qoutes
                if (endOfFilename == startOfFilename + 1) {
                    throw new FormatException(FilenameEmpty);
                }
                // must be exactly two enclosing qoutes
                if (endOfFilename == -1 || switchValue.LastIndexOf("\"") != endOfFilename) {
                    throw new FormatException(string.Format(FilenameNotEnclosedByQuotes, switchValue.Substring(startOfFilename)));
                }
                // filename must be preceeded by 'f'
                if (startOfFilename == 0 || switchValue.Substring(startOfFilename - 1, 1).ToLower() != "f") {
                    throw new FormatException(string.Format(FilenameMustBePreceededByF, switchValue));
                }
                filename = switchValue.Substring(startOfFilename + 1, endOfFilename - startOfFilename - 1);
                sb.Remove(startOfFilename, endOfFilename - startOfFilename + 1);
            }
            int n = sb.ToString().ToLower().IndexOf("f");
            if (n > -1) {
                toFile = true;
                sb.Remove(n, 1);
            }
            n = sb.ToString().ToLower().IndexOf("c");
            if (n > -1) {
                toConsole = true;
                sb.Remove(n, 1);
            }
            if (sb.Length > 0) {
                throw new ArgumentException(sb.ToString());
            }
            return new DisplaySummaryOptions(toConsole, toFile, filename);
        }

        /// <summary>
        ///   Parses SourceSafe user options. Allowed forms (all preceeded by '/u':'):
        ///     [empty]             - username and password will be prompted for
        ///     username            - only username is provided, password is prompted for
        ///     username,           - username, empty password
        ///     username,password   - username and password
        /// </summary>
        /// <param name="switchValue">
        ///   Value to parse.
        /// </param>
        /// <returns>
        ///   <c>SourceSafeUserOptions</c> object initialized with provided 
        ///   switch value(s).
        /// </returns>
        private SourceSafeUserOptions ParseSourceSafeOptions(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            string[] splitNamePswd = switchValue.Split(',');
            switch (splitNamePswd.Length) {
            case 1:
                // if only username provided
                return new SourceSafeUserOptions(splitNamePswd[0]);
            case 2:
                // if username and password provided
                return new SourceSafeUserOptions(splitNamePswd[0], splitNamePswd[1]);
            }
            throw new ArgumentException(string.Format(InvalidSwitchValue, switchValue));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sw"></param>
        /// <param name="textToParse"></param>
        private bool ParseCheckProjectsExistance(string switchValue) {
            Debug.Assert(switchValue != null && switchValue.Length > 0);
            switch (switchValue.ToLower()) {
            case "e":
                return true;
            case "w":
                return false;
            default:
                throw new ArgumentException(string.Format(InvalidSwitchValue, switchValue));
            }
        }

        #endregion // Private methods

        #region Private fields

        private string                  m_solutionFilename;

        private string                  m_newVersion;

        private bool                    m_startGui;

        private bool                    m_applyToAllProjects;

        private AssemblyVersionType     m_applyToTypes;

        private ProjectsSynchronization m_projectsSynchronization;

        private ProjectsNameList        m_projectsAllowedForVersionModification;

        private ProjectsNameList        m_projectsToExclude;

        private ProjectsNameList        m_projectsToForce;

        private DisplaySummaryOptions   m_displaySummary;

        private SourceSafeUserOptions   m_sourceSafeUserOptions;

        private bool                    m_checkProjectsExistance;

        private readonly CommandLineSwitchesTable CommandLineSwitches;

        #endregion // Private fields

        #region String constants

        private const string FileNotFound                   = "File not found: \'{0}\'";
        private const string TooManyArguments               = "Too many arguments provided: \'{0}\'";
        private const string UnknownSwitch                  = "Unknown command line switch: \'{0}\'";
        private const string ErrorInSwitch                  = "Error in switch: \'{0}\': {1}";
        private const string NotEnclosedByQuotes            = "List element \'{1}\' not enclosed by quotes on both sides.";
        private const string FilenameNotEnclosedByQuotes    = "Filename \'{0}\' not enclosed by quotes correctly.";
        private const string DuplicateSwitch                = "Switch \'{0}\' cannot appear multiple times.";
        private const string SwitchHasNoValue               = "No value assigned to switch \'{0}\'.";
        private const string InvalidSwitchFormat            = "Switch \'{0}\' has invalid format: {1}";
        private const string InvalidSwitchValue             = "Switch \'{0}\' is assigned invalid value \'{1}\'.";
        //private const string ValueAlreadyAssigned           = "The same value \'{0}\' already assigned.";
        private const string AsteriskValueMustbeAlone       = "\'*\' must not be used with any additional value.";
        private const string FilenameMustBePreceededByF     = "Filename \'{0}\' must be preceeded by 'f' character.";
        private const string FilenameEmpty                  = "Filename must not be empty string.";
        private const string ProjectNameAlreadySpecified    = "Project name \'{0}\' listed more than once.";
        private const string VersionFlagAlreadySet          = "Flag for \'{0}\' set more than once.";

        #endregion // String constants

        #region Static methods

        public static void OuputDescription(string executableName) {
            CommandLineSwitchesTable.OutputDescription(executableName);
        }

        #endregion // Static methods
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Croatia Croatia
Graduated at the Faculty of Electrical Engineering and Computing, University of Zagreb (Croatia) and received M.Sc. degree in electronics. For several years he was research and lecturing assistant in the fields of solid state electronics and electronic circuits, published several scientific and professional papers, as well as a book "Physics of Semiconductor Devices - Solved Problems with Theory" (in Croatian).
During that work he gained interest in C++ programming language and have co-written "C++ Demystified" (in Croatian), 1st edition published in 1997, 2nd in 2001, 3rd in 2010, 4th in 2014.
After book publication, completely switched to software development, programming mostly in C++ and in C#.
In 2016 coauthored the book "Python for Curious" (in Croatian).

Comments and Discussions