/// <summary> /// Extract option accepted values from object attributes. /// </summary> /// <param name="prop">Property object</param> /// <param name="optionSpec">Option specification object</param> private void ExtractOptionValuesOfAttributes(PropertyInfo prop, ProgramOptionBase optionSpec) { /// <remarks> /// <see cref="ProgramOptionValueAttribute"/> is <see cref="ProgramOptionValuesAttribute"/>. Therefore, listing <see cref="ProgramOptionValueAttribute"/> already included. /// </remarks> IEnumerable<Attribute> valuesAttrList = prop.GetCustomAttributes(typeof(ProgramOptionValuesAttribute)); if (valuesAttrList == null) return; valuesAttrList.ToList().ForEach(v => { IEnumerable<ProgramAcceptedValue> acceptedValues = (v as ProgramOptionValuesAttribute).Values.Select(s => { string[] values = s.Split('|'); if (values.Length < 1) return null; var value = new ProgramAcceptedValue { Value = values.First().Trim() }; if (values.Length > 1) { value.Description = values.Skip(1).First().Trim(); } return value; }).Where(w => w != null); optionSpec.AcceptedValues.AddRange(acceptedValues); }); }
/// <summary> /// Populate object options attributes /// </summary> private void PopulateOptionsAttributes() { foreach (var prop in GetType().GetProperties()) { var optionAttr = prop.GetCustomAttribute(typeof(OptionAttribute)) as OptionAttribute; if (optionAttr != null) { ProgramOptionBase option = Spec.GetOptionByName(optionAttr.Name); if (option == null) { continue; } object optionValue = option.ConvertValueToType(prop.PropertyType); if (optionValue != null) { prop.SetValue(this, optionValue); } } } }
/// <summary> /// Read flags and options values from arguments /// </summary> /// <param name="args">The arguments</param> private void ReadFlagsOptions(string[] args) { var remainingArgs = new List<string>(); int maxArgIdx = args.Length - 1; for (int argc = 0; argc <= maxArgIdx; argc++) { string argOriginal = args[argc]; string arg = argOriginal; // Minimal pattern: "-c" if (arg.Length < 2) { remainingArgs.Add(arg); continue; } char first = arg[0]; char second = arg[1]; bool isFullPattern = false; bool isShortPattern = false; // Full name pattern: "--full-name if (first == '-' && second == '-') { arg = new string(arg.Skip(2).ToArray()); isFullPattern = true; } // Short name pattern: "-name" else if (first == '-') { arg = new string(arg.Skip(1).ToArray()); isShortPattern = true; } // Invalid pattern: "?" if (!isFullPattern && !isShortPattern) { remainingArgs.Add(argOriginal); continue; } string argValue = null; // Pattern: "-x=Value", "--xx=Value" if (arg.Contains('=')) { var eqIdx = arg.IndexOf('='); argValue = arg.Substring(eqIdx + 1); arg = arg.Substring(0, eqIdx); } var argTokens = new List<string>(); // The word is the token itself if (isFullPattern) { argTokens.Add(arg); } // Each character is a token else if (isShortPattern) { arg .ToCharArray() .ToList() .ForEach(c => argTokens.Add(c.ToString())); } argTokens.ForEach(token => { ProgramOptionBase option = Spec.GetOptionByToken(token); // Option not found! if (option == null) { switch (UnknownOptionAction) { case UnknownOptionAction.ThrowException: throw new UnknownOptionException(token, isFullPattern); case UnknownOptionAction.ByPass: if (isFullPattern) { remainingArgs.Add(argOriginal); } else if (argValue != null) { remainingArgs.Add(string.Format("-{0}={1}", token, argValue)); } else { remainingArgs.Add(string.Format("-{0}", token)); } return; // argTokens.ForEach case UnknownOptionAction.Discard: default: return; // argTokens.ForEach } } // Repeated option! else if (option.HasValue) { switch (RepeatedOptionAction) { case RepeatedOptionAction.ThrowException: throw new RepeatedOptionException(token, isFullPattern); case RepeatedOptionAction.Replace: // TODO: ??? // Se não existe @argValue, o valor é o próximo argumento // > Se não existe o próximo argumento, lança exceção de opção inválida? // - aqui podemos ou lançar a exceção, // - ou incluir uma flag que diz se é pra lanaçar a exceção // - ou simplesmente não atribuir o valor da opção if (!option.IsFlag && argValue == null && argc < maxArgIdx) { int nextArgIdx = GetNextValueParameter(argc + 1, args, out argValue); // Found! if (nextArgIdx != argc) { // Removes argument found, and reconfigures context var argList = args.ToList(); argList.RemoveAt(nextArgIdx); args = argList.ToArray(); maxArgIdx--; } } if (!option.IsFlag && argValue != null) { option.InformedValue = argValue; } return; // argTokens.ForEach case RepeatedOptionAction.Ignore: default: return; // argTokens.ForEach } } // Flag found for the first time else if (option.IsFlag) { option.HasValue = true; option.InformedValue = bool.TrueString; } // Option found for the first time else { // TODO: ??? // Se não existe @argValue, o valor é o próximo argumento // > Se não existe o próximo argumento, lança exceção de opção inválida? // - aqui podemos ou lançar a exceção, // - ou incluir uma flag que diz se é pra lanaçar a exceção // - ou simplesmente não atribuir o valor da opção if (argValue == null && argc < maxArgIdx) { int nextArgIdx = GetNextValueParameter(argc + 1, args, out argValue); // Found! if (nextArgIdx != argc) { // Removes argument found, and reconfigures context var argList = args.ToList(); argList.RemoveAt(nextArgIdx); args = argList.ToArray(); maxArgIdx--; } } if (argValue != null) { option.HasValue = true; option.InformedValue = argValue; } } }); } Arguments = remainingArgs.ToArray(); }
private void AddSpecification(string name, ProgramOptionBase option) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } // TODO: Patter is required? if (string.IsNullOrWhiteSpace(option.Pattern)) { throw new ArgumentNullException(nameof(option.Pattern).ToLower()); } // TODO: Description is required? if (string.IsNullOrWhiteSpace(option.Description)) { throw new ArgumentNullException(nameof(option.Description).ToLower()); } // Duplicate key exception by Dictionary _specByName.Add(name, option); // Duplicate key exception by Dictionary option.Pattern.Split('|').ToList().ForEach(keyword => { var argumentException = new ArgumentException(string.Format("Invalid keyword pattern: {0}", keyword)); // Minimal pattern: "-c" if (keyword.Length < 2) { throw argumentException; } char first = keyword[0]; char second = keyword[1]; bool isFullPattern = true; // Full name pattern: "--full-name if (first == '-' && second == '-') { keyword = new string(keyword.Skip(2).ToArray()); } // Short name pattern: "-name" else if (first == '-') { keyword = new string(keyword.Skip(1).ToArray()); isFullPattern = false; } // Invalid pattern: "?" else { throw argumentException; } // Invalid pattern: "--", "-\s", "- " if (string.IsNullOrWhiteSpace(keyword)) { throw argumentException; } if (isFullPattern) { _specByToken.Add(keyword, option); } else { keyword .ToCharArray() .ToList() .ForEach(c => _specByToken.Add(c.ToString(), option)); } }); }