/src/ZebraFlickr/CommandLine/Parser.cs
C# | 221 lines | 158 code | 38 blank | 25 comment | 21 complexity | 0bf3d74881bac502cf85a5396a22a7a5 MD5 | raw file
- #region License
-
- // Copyright (c) 2012 Gareth Lennox (garethl@dwakn.com)
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without modification,
- // are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- // * Neither the name of Gareth Lennox nor the names of its
- // contributors may be used to endorse or promote products derived from this
- // software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
- #endregion
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using ZebraFlickr.CommandLine.Exceptions;
-
- namespace ZebraFlickr.CommandLine
- {
- public static class Parser
- {
- private static readonly IDictionary<Type, ArgumentMap[]> _mapCache = new Dictionary<Type, ArgumentMap[]>();
-
- public static void Parse(object obj, string[] arguments)
- {
- var map = GetMap(obj.GetType()).OrderBy(k => k.Index);
- var args = ParseArgs(map, arguments).ToArray();
-
- var maps = map.ToList();
-
- foreach (var argument in args)
- {
- if (argument.Map != null)
- {
- argument.Map.SetValue(obj, argument);
- maps.Remove(argument.Map);
- }
- }
-
- foreach (var remainingMap in maps)
- {
- if (remainingMap.Required)
- throw new ValidationException(string.Format("Missing required parameter {0}", remainingMap.LongName));
- }
- }
-
- private static IEnumerable<Argument> ParseArgs(IEnumerable<ArgumentMap> map, string[] arguments)
- {
- var bareMap = map.Where(i => i.IsBareOption).ToArray();
- var bareIndex = -1;
-
- for (int x = 0; x < arguments.Length; x++)
- {
- var argument = arguments[x];
-
- if (argument.StartsWith("--")) //long arg
- {
- argument = argument.Substring(2);
- var indexOfEquals = argument.IndexOf('=');
-
- string value = null;
-
- if (indexOfEquals >= 0)
- {
- value = argument.Substring(indexOfEquals + 1);
- argument = argument.Substring(0, indexOfEquals);
- }
-
- var am = map.FirstOrDefault(m => m.LongName == argument || m.OtherLongNames.Contains(argument));
-
- yield return new Argument {Index = x, Key = argument, Value = value, Map = am};
- }
- else if (argument.StartsWith("-")) //short arg
- {
- argument = argument.Substring(1);
- string value = null;
-
- var am = map.FirstOrDefault(m => m.ShortName == argument || m.OtherShortNames.Contains(argument));
-
- if (am != null)
- {
- if (am.HasValue)
- {
- value = arguments[x + 1];
- x++;
- }
-
- yield return new Argument {Index = x, Key = am.LongName, Value = value, Map = am};
- }
- }
- else //command or bare option
- {
- if (bareIndex >= 0)
- {
- if (bareIndex < bareMap.Length)
- {
- var argumentMap = bareMap[bareIndex];
-
- yield return new Argument {Index = x, Key = null, Value = argument, Map = argumentMap};
-
- bareIndex++;
- }
- }
- else
- {
- bareIndex = 0;
-
- yield return new Argument {Index = x, Key = null, Value = argument, Map = null};
- }
- }
- }
- }
-
- public static string GetUsage(Type type)
- {
- return HelpBuilder.GetUsage(GetMap(type), x => !x.IsGeneral);
- }
-
- public static string GetUsage(Type type, Func<DetailsAttribute, bool> filter)
- {
- return HelpBuilder.GetUsage(GetMap(type), filter);
- }
-
- public static string GetBareUsage(Type type)
- {
- return HelpBuilder.GetBareUsage(GetMap(type));
- }
-
- public static string GetBareDetailedUsage(Type type)
- {
- return HelpBuilder.GetBareDetailedUsage(GetMap(type));
- }
-
- public static ArgumentMap[] GetMap(Type type)
- {
- lock (_mapCache)
- {
- ArgumentMap[] value;
- if (_mapCache.TryGetValue(type, out value))
- return value;
- }
-
- var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
-
- IList<ArgumentMap> ret = new List<ArgumentMap>(properties.Length);
-
- foreach (var property in properties)
- {
- var attributes = property.GetCustomAttributes(typeof (DetailsAttribute), true);
-
- if (attributes.Length == 0)
- {
- continue;
- }
-
- foreach (DetailsAttribute detail in attributes)
- {
- var map = new ArgumentMap
- {
- Description = detail.Description,
- ShortName = detail.ShortName,
- LongName = detail.LongName ?? property.Name.ToLowerInvariant(),
- Values = detail.Values,
- Index = detail.Index,
- Property = property,
- Decoder = detail.Decoder,
- OtherLongNames = detail.OtherLongNames,
- OtherShortNames = detail.OtherShortNames,
- Required = detail.Required,
- IsBareOption = detail.IsBareOption,
- Default = detail.Default,
- IsGeneral = detail.IsGeneral
- };
-
- ret.Add(map);
- }
- }
-
- var retArray = (from k in ret
- orderby string.IsNullOrEmpty(k.LongName) ? k.ShortName : k.LongName
- orderby k.IsBareOption ? 1 : 0
- select k)
- .ToArray();
-
- _mapCache[type] = retArray;
-
- return retArray;
- }
-
- #region Nested type: Argument
-
- public class Argument
- {
- public ArgumentMap Map { get; set; }
- public string Key { get; set; }
- public string Value { get; set; }
- public int Index { get; set; }
- }
-
- #endregion
- }
- }