public static Dictionary <int, ValueAttribute> GetValues(Type type) { var result = new Dictionary <int, ValueAttribute>(); foreach (var prop in type.GetProperties()) { foreach (var value in prop.GetCustomAttributes(typeof(ValueAttribute), true)) { ValueAttribute valueAttr = (ValueAttribute)value; if (result.ContainsKey(valueAttr.Index)) { throw new ParserException("There is more than one [Value] with the index " + valueAttr.Index); } if (valueAttr.Index < 0) { throw new ParserException("A [Value]'s index cannot be negative"); } valueAttr.TargetProperty = prop; VerifyAttribute(valueAttr); result.Add(valueAttr.Index, valueAttr); } } if (!result.Keys.SequenceEqual(Enumerable.Range(0, result.Count))) { throw new ParserException("[Value] indices must be consecutive from 0 to N"); } return(result); }
private ParseResult <object> Parse(Type type, string input) { // TODO Refactor this whole thing to use exception handling and store common objects in a class object instance = Activator.CreateInstance(type); Dictionary <string, OptionAttribute> options = OptionsClassProcessor.GetOptions(type); Dictionary <int, ValueAttribute> values = OptionsClassProcessor.GetValues(type); int unsatisfiedCount = options.Count + values.Count; int nextValueToFill = 0; OptionAttribute optionExpectingValue = null; Tokenizer tokenizer = new Tokenizer(input); while (!tokenizer.AtEnd && unsatisfiedCount > 0) { Token token = tokenizer.NextToken; switch (token.Kind) { case TokenKind.LongOption: case TokenKind.ShortOption: if (optionExpectingValue != null) { return(ParseResult <object> .WithError("No value provided for option '" + optionExpectingValue.Option + "'")); } OptionAttribute optionAttr; if (!options.TryGetValue(token.Value, out optionAttr)) { return(ParseResult <object> .WithError("Unexpected option '" + token.Value + "'")); } if (optionAttr.Satisfied) { return(ParseResult <object> .WithError("Multiple values for option '" + token.Value + "' are not allowed")); } var alreadySatisfiedExclusiveOption = FirstAlreadySatisfiedMutuallyExclusiveOption(options, optionAttr); if (alreadySatisfiedExclusiveOption != null) { return(ParseResult <object> .WithError("The option '" + token.Value + "' is mutually exclusive with the option '" + alreadySatisfiedExclusiveOption.Option + "' that was already satisfied")); } if (optionAttr.TargetProperty.PropertyType == typeof(bool)) { optionAttr.TargetProperty.SetValue(instance, true); optionAttr.Satisfied = true; --unsatisfiedCount; } else { optionExpectingValue = optionAttr; } break; case TokenKind.Value: if (optionExpectingValue != null) { string error = SetPropertyValue(optionExpectingValue, instance, token.Value); if (error != null) { return(ParseResult <object> .WithError("Error setting option '" + optionExpectingValue.Option + "': " + error)); } optionExpectingValue.Satisfied = true; optionExpectingValue = null; --unsatisfiedCount; } else { if (nextValueToFill >= values.Count) { // There are no more values to fill, just options return(ParseResult <object> .WithError("Unexpected plain value '" + token.Value + "'")); } ValueAttribute valueAttr = values[nextValueToFill]; string error = SetPropertyValue(valueAttr, instance, token.Value); if (error != null) { return(ParseResult <object> .WithError("Error setting value #" + nextValueToFill + ": " + error)); } valueAttr.Satisfied = true; --unsatisfiedCount; ++nextValueToFill; } break; case TokenKind.Error: return(ParseResult <object> .WithError("Error parsing options: " + token.Value)); } } SetDefaultValues(instance, options, values); var errorResult = FindUnsatisfiedOptionsAndValues(options, values); if (errorResult != null) { return(errorResult); } errorResult = SetRestOfInputPropertyIfAvailable(type, instance, tokenizer); if (errorResult != null) { return(errorResult); } return(ParseResult <object> .WithValue(instance)); }