Parser.cs :  » Development » Command-Line-Option-Parsing » CommandLine » OptParse » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » Command Line Option Parsing 
Command Line Option Parsing » CommandLine » OptParse » Parser.cs
/* This file is part of the CSharpOptParse .NET C# library
 *
 * The library is hosted at http://csharpoptparse.sf.net
 *
 * Copyright (C) 2005 by Andrew Robinson
 *
 * This source code is open source, protected under the GNU GPL Version 2, June 1991
 * Please see http://opensource.org/licenses/gpl-license.php for information and
 * specifics on this license.
 */
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace CommandLine.OptParse{
  #region Parser class
  /// <summary>
  /// Class that parses the command line options. Use the <see cref="ParserFactory"/>
  /// to construct an instance of a parser
  /// <seealso cref="UsageBuilder"/>
  /// <seealso cref="PropertyFieldParserHelper"/>
  /// <seealso cref="DictionaryParserHelper"/>
  /// <seealso cref="IOptionResults"/>
  /// <seealso cref="ParserFactory"/>
  /// </summary>
  /// <remarks>
  /// Class that
  /// </remarks>
  /// <include file="ExternalDocs.xml" 
  ///    path="externalDocs/doc[@name='ExamplePropClass']/*"/>
  public class Parser : IOptionContainer
  {
    #region Events
    /// <summary>
    /// Event fired when a warning event occurs
    /// </summary>
    public event WarningEventHandler OptionWarning;
    #endregion Events

    #region Members
    private DupOptHandleType     _dupOptHandleType = DupOptHandleType.Warning;
    private IOptionResults       _resultsHandler;
    private OptStyle             _optStyle = OptStyle.Unix;
    private UnknownOptHandleType _unknownOptHandleType = UnknownOptHandleType.Warning;
    private UnixShortOption      _unixShortOption = UnixShortOption.ShortSeparated;
    private bool                 _caseSensitive = true;
    private bool                 _searchEnvironment = false;
    #endregion Members
    
    #region Constructor
    /// <summary>
    /// Constructor
    /// <seealso cref="ParserFactory"/>
    /// </summary>
    /// <remarks>
    /// <para>The <paramref name="resultsHandler" /> object is resposible for
    /// maintaining the option definitions and stores the results of parsing.
    /// </para>
    /// <para>The <see cref="ParserFactory"/> class can assist with constructing
    /// instances of the parser (handles <see cref="IOptionResults"/> object
    /// creating). If the factory is not used, the 
    /// <see cref="PropertyFieldParserHelper"/> and
    /// <see cref="DictionaryParserHelper"/> classes can be used.</para>
    /// </remarks>
    /// <param name="resultsHandler">The interface containing the option
    /// definitions and handles the results of parsing.</param>
    public Parser(IOptionResults resultsHandler)
    {
      _resultsHandler = resultsHandler;
    }
    #endregion Constructor

    #region Properties
    /// <summary>
    /// Get or set to search the environment names for values to the options
    /// </summary>
    /// <remarks>
    /// If set to true, environment variable values will be used if found
    /// and the options are not given on the command line. The prefixes
    /// (--, -, or /) are not used, only the names of the options when
    /// searching the environment.
    /// </remarks>
    [DefaultValue(false)]
    public bool SearchEnvironment 
    {
      get { return _searchEnvironment; }
      set { _searchEnvironment = value; }
    }


    /// <summary>
    /// Get or set if the parsing of options should consider the case of the options
    /// </summary>
    [DefaultValue(true)]
    public bool CaseSensitive
    {
      get { return _caseSensitive; }
      set { _caseSensitive = value; }
    }


    /// <summary>
    /// Get or set how the parser should handle un-expected duplicate option declarations
    /// </summary>
    [DefaultValue(DupOptHandleType.Warning)]
    public DupOptHandleType DupOptHandleType 
    {
      get { return _dupOptHandleType; }
      set { _dupOptHandleType = value; }
    }


    /// <summary>
    /// Get the handler for the parser
    /// </summary>
    public IOptionResults ResultsHandler 
    {
      get { return _resultsHandler; }
    }


    /// <summary>
    /// Get or set the option style to parse
    /// </summary>
    public OptStyle OptStyle 
    {
      get { return _optStyle; }
      set { _optStyle = value; }
    }


    /// <summary>
    /// Get or set how to handle unknown options
    /// </summary>
    [DefaultValue(UnknownOptHandleType.Warning)]
    public UnknownOptHandleType UnknownOptHandleType 
    {
      get { return _unknownOptHandleType; }
      set { _unknownOptHandleType = value; }
    }


    /// <summary>
    /// Get how to handle short unix options (with or without bundling)
    /// </summary>
    [DefaultValue(UnixShortOption.ShortSeparated)]
    public UnixShortOption UnixShortOption 
    {
      get { return _unixShortOption; }
      set { _unixShortOption = value; }
    }
    #endregion Properties

    #region Public methods
    /// <summary>
    /// Get all of the option definitions that have been declared
    /// </summary>
    /// <returns>Array of option definitions</returns>
        public OptionDefinition[] GetOptionDefinitions()
    {
      return _resultsHandler.GetOptions();
    }


    /// <summary>
    /// Get a collection of option value, option description pairs
    /// </summary>
    /// <remarks>
    /// Used to help print the usage of the application. This function concatinates
    /// the options together into one string that is used as the key of the
    /// collection, and then sets the value as the description of the option.
    /// <para>
    /// Example result:
    /// <code>
    /// Key  : -h, --help
    ///  Value: Print the usage of this application
    /// </code>
    /// Here, the key is a comma concatination of the options and the value
    /// is the description of that option.
    /// </para>
    /// <para>
    /// Short names will precede the long options in the key.
    /// </para>
    /// </remarks>
    /// <param name="style">The style of options. During concatination, the
    /// appropriate prefix ('/', '-' or '--') will be added to each option
    /// name</param>
    /// <returns>Collection of the options and their descriptions</returns>
    [Obsolete("Use UsageBuilder instead")]
    public NameValueCollection GetOptionDescriptions(OptStyle style)
    {
      NameValueCollection coll = new NameValueCollection();
      OptionDefinition[]  opts = _resultsHandler.GetOptions();
      StringBuilder       key  = new StringBuilder();

      foreach (OptionDefinition opt in opts)
      {
        key.Length = 0;

        if (opt.ShortNames != null && opt.ShortNames.Length > 0)
        {
          foreach (char optName in opt.ShortNames)
          {
            if (key.Length > 0) 
              key.Append((style == OptStyle.Unix) ? ", -" : ", /");
            else
              key.Append((style == OptStyle.Unix) ? "-" : "/");
            key.Append(optName);
          }

          if (opt.LongNames != null && opt.LongNames.Length > 0)
            key.Append(", ");
        }

        if (opt.LongNames != null && opt.LongNames.Length > 0)
        {
          key.Append((style == OptStyle.Unix) ? "--" : "/");
          key.Append(string.Join((style == OptStyle.Unix) ? ", --" : ", /",
            opt.LongNames));
        }

        coll.Add(key.ToString(), opt.Description);
      }

      return coll;
    }

    
    /// <summary>
    /// Prints out the usage of this program to the given text writer
    /// <seealso cref="IProgUsageInfo"/>
    /// <seealso cref="DefaultUsageInfo"/>
    /// <seealso cref="GetOptionDescriptions"/>
    /// </summary>
    /// <remarks>
    /// Prints the usage to the standard output. See <see cref="IProgUsageInfo"/>
    /// and <see cref="GetOptionDescriptions"/>
    /// for more information on how the usage is printed. 
    /// <para>At this time, wrapping is not done at word boundries</para>
    /// </remarks>
    /// <param name="optStyle">The style of the options to have printed
    /// in the usage</param>
    /// <param name="usageInfo"></param>
    /// <param name="columns">The number of columns to print before wrapping
    /// the text. Used to correctly format the indenting</param>
    [Obsolete("Use UsageBuilder instead")]
    public void PrintUsage(OptStyle optStyle, IProgUsageInfo usageInfo, 
      int columns)
    {
      PrintUsage(optStyle, usageInfo, Console.Out, columns);
    }


    /// <summary>
    /// Prints out the usage of this program to the given text writer
    /// <seealso cref="IProgUsageInfo"/>
    /// <seealso cref="DefaultUsageInfo"/>
    /// <seealso cref="GetOptionDescriptions"/>
    /// </summary>
    /// <remarks>
    /// Prints the usage to the given writer. See <see cref="IProgUsageInfo"/>
    /// and <see cref="GetOptionDescriptions"/>
    /// for more information on how the usage is printed. 
    /// <para>At this time, wrapping is not done at word boundries</para>
    /// </remarks>
    /// <param name="optStyle">The style of the options to have printed
    /// in the usage</param>
    /// <param name="writer">The writer to print the usage to</param>
    /// <param name="usageInfo"></param>
    /// <param name="columns">The number of columns to print before wrapping
    /// the text. Used to correctly format the indenting</param>
    [Obsolete("Use UsageBuilder instead")]
    public void PrintUsage(OptStyle optStyle, IProgUsageInfo usageInfo, 
      TextWriter writer, int columns)
    {
      foreach (string header in usageInfo.Headers)
      {
        writer.WriteLine(header);
        if (usageInfo.IsOptionHeader(header))
        {
          NameValueCollection coll = GetOptionDescriptions(optStyle);

          for (int i = 0; i < coll.Count; i++)
          {
            PrintWithIndent(4, coll.GetKey(i), columns, writer);
            PrintWithIndent(8, coll[i], columns, writer);
          }
        }
        else
          PrintWithIndent(4, usageInfo.GetContents(header), columns, writer);
      }
    }
    #endregion Public methods

    #region Parse Methods
    /// <summary>
    /// Parse the <see cref="Environment.GetCommandLineArgs"/> 
    /// for options using the parser settings
    /// </summary>
    /// <returns>Arguments from the command-line that were not options</returns>
    public string[] Parse()
    {
      return Parse(this.OptStyle, this.UnixShortOption, this.DupOptHandleType,
        this.UnknownOptHandleType, this.CaseSensitive, Environment.GetCommandLineArgs());
    }


    /// <summary>
    /// Parse the arguments for options using the parser settings
    /// </summary>
    /// <param name="args">The command-line arguments to parse</param>
    /// <returns>Arguments from the command-line that were not options</returns>
    public string[] Parse(string[] args)
    {
      return Parse(this.OptStyle, this.UnixShortOption, this.DupOptHandleType,
        this.UnknownOptHandleType, this.CaseSensitive, args);
    }


    /// <summary>
    /// Parse the arguments for options using the given settings
    /// </summary>
    /// <param name="optStyle">What type of options to parse</param>
    /// <param name="unixShortOption">How to parse unix short options (ignored
    /// if not unix style parsing)</param>
    /// <param name="dupOptHandleType">How to handle un-expected duplicate option
    /// definitions</param>
    /// <param name="unknownOptHandleType">How to handle options that were not
    /// defined</param>
    /// <param name="caseSesitive">If the parsing of options 
    /// should consider the case of the options</param>
    /// <param name="args">The command-line arguments to parse</param>
    /// <returns>Arguments from the command-line that were not options</returns>
    public string[] Parse(OptStyle optStyle, UnixShortOption unixShortOption, 
      DupOptHandleType dupOptHandleType, UnknownOptHandleType unknownOptHandleType,
      bool caseSesitive, string[] args)
    {
      ArrayList        foundDefinitions = new ArrayList();
      Group            grp;
      Match            m;
      Regex            re;
      StringCollection arguments;
      bool             isShort;
      bool             unknown;
      string           name;
      string           value;
      string[]         results;

      if (args == null || args.Length == 0)
      {
        if (this.SearchEnvironment)
          ParseEnvironment(foundDefinitions, caseSesitive);
        return new string[0];
      }

      arguments = new StringCollection();
      
      re = BuildRegEx(optStyle, unixShortOption);

      for (int i = 0; i < args.Length; i++)
      {
        m = re.Match(args[i]);

        if (m.Success)
        {
          // see if there is a value
          grp = m.Groups["shortname"];

          if (grp != null && grp.Value != string.Empty)
          {
            isShort = true;
            name = grp.Value;
            value = m.Groups["shortvalue"].Value;
          }
          else
          {
            isShort = false;
            name = m.Groups["longname"].Value;
            value = m.Groups["longvalue"].Value;

            // remove the '=' or ':' at the beginning of the value
            if (value != null && value.Length > 0)
              value = value.Substring(1);
          }

          if (optStyle == OptStyle.Unix)
          {
            if (isShort == false)
              ParseLongOption(args, name, value, caseSesitive, 
                ref i, dupOptHandleType, unknownOptHandleType, foundDefinitions,
                out unknown);
            // see if the value is just concatinated short options
            else if (unixShortOption == UnixShortOption.CollapseShort)
            {
              // name is the first option
              ParseShortOption(args, name[0], null, caseSesitive, 
                ref i, dupOptHandleType, unknownOptHandleType, foundDefinitions,
                out unknown);

              // parse the rest of the options that lie in the 'value' variable
              if (value != null)
              {
                char[] unknownOptions;

                ParseShortOptions(args, value.ToLower().ToCharArray(), 
                  caseSesitive, ref i, dupOptHandleType, unknownOptHandleType,
                  foundDefinitions, out unknownOptions);

                // don't do anything with unknown concatinated short options,
                // they should not be considered options or arguments
              }
            }
            else
              // name is the first option
              ParseShortOption(args, name[0], value, caseSesitive,
                ref i, dupOptHandleType, unknownOptHandleType, foundDefinitions,
                out unknown);
          }
          else
          {
            if (name.Length == 1)
              ParseShortOption(args, name[0], value, caseSesitive,
                ref i, dupOptHandleType, unknownOptHandleType, foundDefinitions,
                out unknown);
            else
              ParseLongOption(args, name, value, caseSesitive, ref i, 
                dupOptHandleType, unknownOptHandleType, foundDefinitions,
                out unknown);
          }

          // consider unknown options to be arguments
          if (unknown)
            arguments.Add(args[i]);
        }
        else
          arguments.Add(args[i]);
      }

      
      // parse the environment
      if (this.SearchEnvironment)
        ParseEnvironment(foundDefinitions, caseSesitive);

      results = new string[arguments.Count];
      arguments.CopyTo(results, 0);
      return results;
    }
    #endregion Parse Methods

    #region Private methods
    private void ParseEnvironment(ArrayList foundDefinitions, bool caseSensitive)
    {
      OptionDefinition opt;
      OptionResult     result;

      foreach (string key in Environment.GetEnvironmentVariables().Keys)
      {
        string value;

        if (key.Length == 1)
                    opt = _resultsHandler.GetOptionDefinition(key[0], caseSensitive);
        else
          opt = _resultsHandler.GetOptionDefinition(key, caseSensitive);

        if (opt != null)
        {
          // don't not overwrite the command-line values
          if (foundDefinitions.Contains(opt))
            continue;

          value  = Environment.GetEnvironmentVariable(key);
          result = _resultsHandler[opt];

          if (result == null) result = new OptionResult(opt);

          switch (opt.Type)
          {
            case OptValType.Flag:
              if (value.ToLower() != bool.TrueString.ToLower()) continue;
              result.NumDefinitions = 1;
              _resultsHandler[opt] = result;
              break;

            case OptValType.IncrementalFlag:
              if (value.ToLower() != bool.TrueString.ToLower()) continue;
              result.NumDefinitions++;
              result.Value = result.NumDefinitions;
              _resultsHandler[opt] = result;
              break;

            case OptValType.MultValue:
              if (value == null) continue;
              try { result.AddValue(opt.ConvertValue(value)); }
              catch { continue; }
              result.NumDefinitions++;
              _resultsHandler[opt] = result;
              break;

            case OptValType.ValueOpt:
              if (value == null) continue;
              try { result.Value = opt.ConvertValue(value); }
              catch { continue; }
              result.NumDefinitions = 1;
              _resultsHandler[opt] = result;
              break;

            case OptValType.ValueReq:
              if (value == null) continue;
              try { result.Value = opt.ConvertValue(value); }
              catch { continue; }
              result.NumDefinitions = 1;
              _resultsHandler[opt] = result;
              break;
          }
        }
      }
    }


    /// <summary>
    /// Prints text to the given writer with the given indent
    /// </summary>
    /// <remarks>At this time wrapping is not done at a word boundry</remarks>
    /// <param name="indent">The indent to add to the text in terms of number
    /// of spaces</param>
    /// <param name="text">The text to print</param>
    /// <param name="maxColumns">The number of columns to wrap at</param>
    /// <param name="writer">The writer to print to</param>
    private void PrintWithIndent(int indent, string text, int maxColumns,
      TextWriter writer)
    {
      string   indentStr = new string(' ', indent);
      string[] lines;

      text = text.Replace("\t", "    ");
      text = text.Replace("\r\n", "\n");
      text = text.Replace("\r", "\n");

      lines = text.Split('\n');
      maxColumns = maxColumns - indent;

      foreach (string line in lines)
      {
        if (text.Length <= maxColumns)
        {
          writer.Write(indentStr);
          writer.WriteLine(line);
        }
        else
        {
          for (int currPos = 0; currPos < line.Length;)
          {
            int    i = Math.Min(maxColumns, line.Length - currPos);
            string str = line.Substring(currPos, i);

            writer.Write(indentStr);
            writer.WriteLine(str);

            currPos += i;
          }
        }
      }
    }


    /// <summary>
    /// Parse a concatinated list of short options
    /// </summary>
    /// <param name="args">All the arguments</param>
    /// <param name="options">The options found</param>
    /// <param name="index">The current index in the arguments which can be incremented
    /// by this function</param>
    /// <param name="dupOptHandleType">How to handle un-expected duplicate option
    /// definitions</param>
    /// <param name="unknownOptHandleType">How to handle options that were not
    /// defined</param>
    /// <param name="caseSesitive">True if parsing is case-sensitive</param>
    /// <param name="foundDefinitions">The option definitions already found</param>
    /// <param name="unknownChars">An array of all unknown option characters or null
    /// if all were known</param>
    private void ParseShortOptions(string[] args, char[] options, bool caseSesitive, ref int index,
      DupOptHandleType dupOptHandleType, UnknownOptHandleType unknownOptHandleType,
      ArrayList foundDefinitions, out char[] unknownChars)
    {
      ArrayList unknownCharsList = new ArrayList();
      bool      wasUnknown;

      foreach (char option in options)
      {
        ParseShortOption(args, option, null, caseSesitive, ref index, dupOptHandleType,
          unknownOptHandleType, foundDefinitions, out wasUnknown);

        if (wasUnknown)
          unknownCharsList.Add(option);
      }

      if (unknownCharsList.Count == 0)
        unknownChars = null;
      else
        unknownChars = (char[])unknownCharsList.ToArray(typeof(char));
    }


    /// <summary>
    /// Parse a short option
    /// </summary>
    /// <param name="args">All the arguments</param>
    /// <param name="option">The option name found</param>
    /// <param name="value">The value immediately following the option (no space)</param>
    /// <param name="index">The current index in the arguments which can be incremented
    /// by this function</param>
    /// <param name="dupOptHandleType">How to handle un-expected duplicate option
    /// definitions</param>
    /// <param name="unknownOptHandleType">How to handle options that were not
    /// defined</param>
    /// <param name="caseSesitive">True if parsing is case-sensitive</param>
    /// <param name="foundDefinitions">The option definitions already found</param>
    /// <param name="wasUnknown">If the option was unknown and the handle type was not
    /// error, this will be true so that the "option" can be added to the arguments</param>
    private void ParseShortOption(string[] args, char option, string value, bool caseSesitive, 
      ref int index, DupOptHandleType dupOptHandleType, 
      UnknownOptHandleType unknownOptHandleType, ArrayList foundDefinitions, out bool wasUnknown)
    {
      ParseOption(args, option.ToString(), 
        _resultsHandler.GetOptionDefinition(option, caseSesitive), 
        value, ref index, dupOptHandleType, unknownOptHandleType, foundDefinitions,
        out wasUnknown);
    }


    /// <summary>
    /// Parse a long option
    /// </summary>
    /// <param name="args">All the arguments</param>
    /// <param name="option">The option name found</param>
    /// <param name="value">The value immediately following the option ([=:]value syntax)</param>
    /// <param name="index">The current index in the arguments which can be incremented
    /// by this function</param>
    /// <param name="dupOptHandleType">How to handle un-expected duplicate option
    /// definitions</param>
    /// <param name="unknownOptHandleType">How to handle options that were not
    /// defined</param>
    /// <param name="caseSesitive">True if parsing is case-sensitive</param>
    /// <param name="foundDefinitions">The option definitions already found</param>
    /// <param name="wasUnknown">If the option was unknown and the handle type was not
    /// error, this will be true so that the "option" can be added to the arguments</param>
    private void ParseLongOption(string[] args, string option, string value, bool caseSesitive, 
      ref int index, DupOptHandleType dupOptHandleType, 
      UnknownOptHandleType unknownOptHandleType, ArrayList foundDefinitions, out bool wasUnknown)
    {
      ParseOption(args, option, _resultsHandler.GetOptionDefinition(option, caseSesitive), 
        value, ref index, dupOptHandleType, unknownOptHandleType, foundDefinitions,
        out wasUnknown);
    }


    /// <summary>
    /// Parse an option
    /// </summary>
    /// <param name="args">All the arguments</param>
    /// <param name="optName">The char or string that the option was identified with</param>
    /// <param name="optDef">The option found</param>
    /// <param name="value">The value immediately following the option ([=:]value syntax)</param>
    /// <param name="index">The current index in the arguments which can be incremented
    /// by this function</param>
    /// <param name="dupOptHandleType">How to handle un-expected duplicate option
    /// definitions</param>
    /// <param name="unknownOptHandleType">How to handle options that were not
    /// defined</param>
    /// <param name="foundDefinitions">The option definitions already found</param>
    /// <param name="wasUnknown">If the option was unknown and the handle type was not
    /// error, this will be true so that the "option" can be added to the arguments</param>
    private void ParseOption(string[] args, object optName,
      OptionDefinition optDef, string value, 
      ref int index, DupOptHandleType dupOptHandleType, 
      UnknownOptHandleType unknownOptHandleType, ArrayList foundDefinitions, out bool wasUnknown)
    {
      OptionResult result;

      // make sure the option is found
      if (optDef == null)
      {
        switch (unknownOptHandleType)
        {
          case UnknownOptHandleType.Error:
            throw new ParseException(
              string.Format("Option {0} is not recoginzed", optName));
          case UnknownOptHandleType.Warning:
            if (OptionWarning != null)
              OptionWarning(this, new OptionWarningEventArgs(
                string.Format("Option {0} is not recoginzed", optName)));
            break;
        }

        wasUnknown = true;
        return;
      }
      else
        wasUnknown = false;

      // make sure value is not given went it should not be given
      CheckIfValueless(optDef, value);

      result = _resultsHandler[optDef];

      // make sure value is not duplicate when it should not be
      CheckIfDup(optDef, foundDefinitions, dupOptHandleType);
      foundDefinitions.Add(optDef);

      switch (optDef.Type)
      {
        case OptValType.Flag:
          if (result == null)
            result = new OptionResult(optDef);

          result.NumDefinitions = 1;
          result.Value = true;
          _resultsHandler[optDef] = result;
          break;

        case OptValType.IncrementalFlag:
          if (result == null)
            result = new OptionResult(optDef);

          result.NumDefinitions++;
          result.Value = result.NumDefinitions;
          _resultsHandler[optDef] = result;
          break;

        case OptValType.MultValue:
          value = GetValue(args, value, ref index);

          if (value == null)
            throw new ParseException(
              string.Format("Option {0} requires a value", optDef.ID));

          if (result == null)
            result = new OptionResult(optDef);

          result.NumDefinitions++;
          result.AddValue(optDef.ConvertValue(value));
          _resultsHandler[optDef] = result;
          break;

        case OptValType.ValueOpt:
          value = GetValue(args, value, ref index);

          if (result == null)
            result = new OptionResult(optDef);

          result.NumDefinitions = 1;
          result.Value = optDef.ConvertValue(value);
          _resultsHandler[optDef] = result;
          break;

        case OptValType.ValueReq:
          value = GetValue(args, value, ref index);

          if (value == null)
            throw new ParseException(
              string.Format("Option {0} requires a value", optDef.ID));
          
          if (result == null)
            result = new OptionResult(optDef);

          result.NumDefinitions = 1;
          result.Value = optDef.ConvertValue(value);
          _resultsHandler[optDef] = result;
          break;
      }
    }


    private string GetValue(string[] args, string value, ref int index)
    {
      if (value == null || value == string.Empty)
      {
        if (index + 1 >= args.Length)
          return null;
                
        value = args[index + 1];

        // make sure an option is not next
        if (value.StartsWith("-") || value.StartsWith("/"))
          return null;

        value = args[++index];
      }

      return value;
    }


    /// <summary>
    /// Check if the option is a duplicate (2nd, 3rd, etc. declaration) and the option
    /// does not support multiple values/declarations
    /// </summary>
    /// <param name="optDef">The option found</param>
    /// <param name="foundDefinitions">All of the found definitions so far</param>
    /// <param name="dupOptHandleType">How to handle duplicates</param>
    /// <returns>True if the option has already been found, and the type
    /// does not support duplicates</returns>
    private bool CheckIfDup(OptionDefinition optDef, ArrayList foundDefinitions, 
      DupOptHandleType dupOptHandleType)
    {
      if (foundDefinitions.IndexOf(optDef) == -1) return false;

      switch (optDef.Type)
      {
          // types that allow duplicates:
        case OptValType.MultValue:
        case OptValType.IncrementalFlag:
          return false;
        default:
          switch (dupOptHandleType)
          {
            case DupOptHandleType.Error:  
              throw new ParseException(
                string.Format("Option {0} does not support duplicate values",
                optDef.ID));
            case DupOptHandleType.Warning:
              if (OptionWarning != null)
                OptionWarning(this, new OptionWarningEventArgs(
                  string.Format("Option {0} does not support duplicate values",
                optDef.ID)));
              break;
          }
          return true;
      }
    }



    /// <summary>
    /// Check if the option definition does not allow a value and if a value was given
    /// </summary>
    /// <param name="optDef">The option definition</param>
    /// <param name="value">The value given or null if one was not given</param>
    /// <exception cref="ParseException">Thrown if value was given to a type
    /// that doesn't support a value</exception>
    private void CheckIfValueless(OptionDefinition optDef, string value)
    {
      // make sure value is not null (or empty)
      if (value == null || value == string.Empty)
        return;

      switch (optDef.Type)
      {
          // types that allow values:
        case OptValType.MultValue:
        case OptValType.ValueOpt:
        case OptValType.ValueReq:
          return;
        default:
          throw new ParseException(
            string.Format("Value given to option {0} that does not support a value",
            optDef.ID));
      }
    }


    private Regex BuildRegEx(OptStyle optStyle, UnixShortOption unixShortOption)
    {
      if (optStyle == OptStyle.Unix)
        return new Regex(@"^
          (?:
            --(?<longname>[^\s=]+)
            (?<longvalue>=.+)?
            |
            -(?<shortname>\S)
            (?<shortvalue>.+)?
          )$", RegexOptions.IgnorePatternWhitespace);
      else
        return new Regex(@"^/(?<longname>[^\s:]+)(?<longvalue>:.+)?$");
    }
    #endregion Private methods

    #region IOptionContainer Members
    /// <summary>
    /// Get the options defined for this parser
    /// </summary>
    /// <returns>All of the defined options</returns>
    public OptionDefinition[] GetOptions()
    {
      return _resultsHandler.GetOptions();
    }
    #endregion
  }
  #endregion Parser class

  #region OptionWarningEventArgs class
  /// <summary>
  /// Event args for parse warnings
  /// </summary>
  public class OptionWarningEventArgs : EventArgs
  {
    #region Members
    private string _warningMessage;
    #endregion Members

    #region Constructor
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="warningMessage">The warning message</param>
    public OptionWarningEventArgs(string warningMessage)
    {
      _warningMessage = warningMessage;
    }
    #endregion Constructor

    #region Properties
    /// <summary>
    /// Get the warning message
    /// </summary>
    public string WarningMessage 
    {
      get { return _warningMessage; }
    }
    #endregion Properties
  }
  #endregion OptionWarningEventArgs class

  #region ParserFactory class
  /// <summary>
  /// Factory to build parser instances
  /// <seealso cref="Parser"/>
  /// <seealso cref="ParserFactory"/>
  /// <seealso cref="UsageBuilder"/>
  /// </summary>
  public sealed class ParserFactory
  {
    #region Constructor
    private ParserFactory() {}
    #endregion Constructor

    #region Factory methods
    /// <summary>
    /// Build a parser
    /// </summary>
    /// <param name="propertiesObject">Object containing fields and properties
    /// that represent option definitions</param>
    /// <returns>Parser instance</returns>
    public static Parser BuildParser(object propertiesObject)
    {
      return new Parser(new PropertyFieldParserHelper(propertiesObject));
    }

  
    /// <summary>
    /// Build a parser
    /// </summary>
    /// <param name="optDefs">The supported options</param>
    /// <param name="valuesDictionary">Dictionary to recieve parsed option values</param>
    /// <returns>Parser instance</returns>
    public static Parser BuildParser(OptionDefinition[] optDefs, IDictionary valuesDictionary)
    {
      return new Parser(new DictionaryParserHelper(optDefs, valuesDictionary));
    }


    /// <summary>
    /// Build a parser
    /// </summary>
    /// <param name="optDefs">The supported options</param>
    /// <param name="valuesDictionary">The dictionary to store the option results in</param>
    /// <returns>Parser instance</returns>
    public static Parser BuildParser(OptionDefinition[] optDefs, 
      OptionResultsDictionary valuesDictionary)
    {
      return new Parser(new DictionaryParserHelper(optDefs, valuesDictionary));
    }


    /// <summary>
    /// Build a parser using a custom <see cref="IOptionResults"/> handler.
    /// </summary>
    /// <param name="resultsHandler">Custom handler</param>
    /// <returns>Parser instance</returns>
    public static Parser BuildParser(IOptionResults resultsHandler)
    {
      return new Parser(resultsHandler);
    }
    #endregion Factory methods
  }
  #endregion ParserFactory class
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.