Ejemplo n.º 1
0
        /// <summary>
        /// Creates a dictionary that is populated with key/value pairs from a command line
        /// that supports syntax where options are provided in the form "/key=value".
        /// This method supports the ability to specify delimiter characters for options in
        /// the command line.
        /// </summary>
        /// <param name="arguments">Key/value pairs.</param>
        /// <param name="keyCharacter">A character that precedes a key.</param>
        /// <param name="valueCharacter">A character that separates a key from a value.</param>
        /// <returns></returns>
        public static CommandLineDictionary FromArguments(IEnumerable <string> arguments, char keyCharacter, char valueCharacter)
        {
            CommandLineDictionary cld = new CommandLineDictionary();

            cld.KeyCharacter   = keyCharacter;
            cld.ValueCharacter = valueCharacter;
            foreach (string argument in arguments)
            {
                cld.AddArgument(argument);
            }

            return(cld);
        }
Ejemplo n.º 2
0
        /// <summary>
        ///     Sets properties on an object from a series of key/value string
        ///     arguments that are in the form "/PropertyName=Value", where the
        ///     value is converted from a string into the property type.
        /// </summary>
        /// <param name="valueToPopulate">The object to set properties on.</param>
        /// <param name="args">The key/value arguments describing the property names and values to set.</param>
        /// <returns>
        ///     Indicates whether the properties were successfully set.  Reasons for failure reasons include
        ///     a property name that does not exist or a value that cannot be converted from a string.
        /// </returns>
        /// <exception cref="System.ArgumentException">Thrown when one of the key/value strings cannot be parsed into a property.</exception>
        public static void ParseArguments(this object valueToPopulate, IEnumerable <string> args)
        {
            CommandLineDictionary        commandLineDictionary = CommandLineDictionary.FromArguments(args);
            PropertyDescriptorCollection properties            = TypeDescriptor.GetProperties(valueToPopulate);

            // Ensure required properties are specified.
            foreach (PropertyDescriptor property in properties)
            {
                // See whether any of the attributes on the property is a RequiredAttribute.
                if (property.Attributes.Cast <Attribute>().Any(attribute => attribute is RequiredAttribute))
                {
                    // If so, and the command line dictionary doesn't contain a key matching
                    // the property's name, it means that a required property isn't specified.
                    if (!commandLineDictionary.ContainsKey(property.Name))
                    {
                        throw new ArgumentException("A value for the " + property.Name + " property is required.");
                    }
                }
            }

            foreach (KeyValuePair <string, string> keyValuePair in commandLineDictionary)
            {
                // Find a property whose name matches the kvp's key, ignoring case.
                // We can't just use the indexer because that is case-sensitive.
                PropertyDescriptor property = MatchProperty(keyValuePair.Key, properties, valueToPopulate.GetType());

                // If the value is null/empty and the property is a bool, we
                // treat it as a flag, which means its presence means true.
                if (string.IsNullOrEmpty(keyValuePair.Value) && (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?)))
                {
                    property.SetValue(valueToPopulate, true);
                    continue;
                }

                object valueToSet;

                // We support a limited set of collection types. Setting a List<T>
                // is one of the most flexible types as it supports three different
                // interfaces, but the catch is that we don't support the concrete
                // Collection<T> type. We can expand it to support Collection<T>
                // in the future, but the code will get a bit uglier.
                switch (property.PropertyType.Name)
                {
                case "IEnumerable`1":
                case "ICollection`1":
                case "IList`1":
                case "List`1":
                    MethodInfo methodInfo        = typeof(CommandLineParser).GetMethod("FromCommaSeparatedList", BindingFlags.Static | BindingFlags.NonPublic);
                    Type[]     genericArguments  = property.PropertyType.GetGenericArguments();
                    MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
                    valueToSet = genericMethodInfo.Invoke(null, new object[] { keyValuePair.Value });
                    break;

                default:
                    TypeConverter typeConverter = TypeDescriptor.GetConverter(property.PropertyType);
                    if (typeConverter == null || !typeConverter.CanConvertFrom(typeof(string)))
                    {
                        throw new ArgumentException("Unable to convert from a string to a property of type " + property.PropertyType + ".");
                    }

                    valueToSet = typeConverter.ConvertFrom(keyValuePair.Value);
                    break;
                }

                property.SetValue(valueToPopulate, valueToSet);
            }
        }