/// <summary>
        /// Initializes a new instance of the <see cref="Option"/> class.
        /// </summary>
        /// <param name="attribute">The attribute describing this option.</param>
        /// <param name="memberInfo">The <see cref="MemberInfo"/> object pointing to the member to which the attribute was applied.</param>
        /// <param name="cmdLineObject">The command line manager object.</param>
        /// <param name="optionGroups">A complete list of all available option groups.</param>
        /// <param name="numberFormatInfo">The number format info to use for parsing numerical arguments.</param>
        public Option(CommandLineOptionAttribute attribute, MemberInfo memberInfo, object cmdLineObject,
                      ICollection <OptionGroup> optionGroups, NumberFormatInfo numberFormatInfo)
        {
            mObject           = cmdLineObject;
            mMember           = memberInfo;
            mUsage            = attribute.BoolFunction;
            mDescription      = attribute.Description;
            mNumberFormatInfo = numberFormatInfo ?? CultureInfo.CurrentCulture.NumberFormat;
            mDefaultValue     = attribute.DefaultAssignmentValue;
            mMinValue         = attribute.MinValue;
            mMaxValue         = attribute.MaxValue;

            // Check the validity of the member for which this attribute was defined
            switch (memberInfo.MemberType)
            {
            case MemberTypes.Field:
                FieldInfo fieldInfo = (FieldInfo)memberInfo;
                if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal field for this attribute; field must be writeable");
                }

                mOptionType = fieldInfo.FieldType;
                break;

            case MemberTypes.Method:
                MethodInfo      method     = (MethodInfo)memberInfo;
                ParameterInfo[] parameters = method.GetParameters();

                if (parameters.Length != 1)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal method for this attribute; the method must accept exactly one parameter");
                }

                if (parameters[0].IsOut)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal method for this attribute; the parameter of the method must not be an out parameter");
                }

                if (IsArray(parameters[0].ParameterType) || IsCollectionType(parameters[0].ParameterType))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal method for this attribute; the parameter of the method must be a non-array and non-collection type");
                }

                mOptionType = parameters[0].ParameterType;
                break;

            case MemberTypes.Property:
                PropertyInfo propInfo = (PropertyInfo)memberInfo;

                if (!propInfo.CanWrite && !IsCollectionType(propInfo.PropertyType))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal property for this attribute; property for non-collection type must be writable");
                }

                if (!propInfo.CanRead && IsCollectionType(propInfo.PropertyType))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal property for this attribute; property for collection type must be readable");
                }

                if (!(propInfo.CanRead && propInfo.CanWrite) && IsArray(propInfo.PropertyType))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                                 "Illegal property for this attribute; property representing array type must be both readable and writeable");
                }

                mOptionType = propInfo.PropertyType;
                break;

            default:
                throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                             "Illegal member for this attribute; member must be a property, method (accepting one parameter) or a field");
            }

            mMinOccurs = attribute.MinOccurs;

            // MaxOccurs does not have a default value (since this is different for various types), so we set it here.
            if (!attribute.IsMaxOccursSet)
            {
                // Use default setting for MaxOccurs
                if (IsArray(mOptionType) || IsCollectionType(mOptionType))
                {
                    mMaxOccurs = 0; // Unlimited
                }
                else
                {
                    mMaxOccurs = 1;
                }
            }
            else
            {
                mMaxOccurs = attribute.MaxOccurs;
            }

            if (mMinOccurs > mMaxOccurs && mMaxOccurs > 0)
            {
                throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                             String.Format(CultureInfo.CurrentUICulture, "MinOccurs ({0}) must not be larger than MaxOccurs ({1})", mMinOccurs, mMaxOccurs));
            }

            if (mMaxOccurs != 1 && !(IsArray(mOptionType) || IsCollectionType(mOptionType)) && mMember.MemberType != MemberTypes.Method)
            {
                throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                                             "Invalid cardinality for member; MaxOccurs must be equal to one (1) for any non-array or non-collection type");
            }

            CommandLineManagerAttribute objectAttr = (CommandLineManagerAttribute)Attribute.GetCustomAttribute(mObject.GetType(), typeof(CommandLineManagerAttribute));

            if (objectAttr == null)
            {
                throw new AttributeException(String.Format(CultureInfo.CurrentUICulture,
                                                           "Class {0} contains a CommandLineOptionAttribute, but does not have the attribute CommandLineObjectAttribute", mObject.GetType().FullName));
            }


            // Assign the name of this option from the member itself if no name is explicitly provided
            if (attribute.Name == null)
            {
                mName = memberInfo.Name;
            }
            else
            {
                mName = attribute.Name;
            }

            // Find the group (if any) that this option belongs to in the list of available option groups
            if (attribute.GroupId != null)
            {
                if (!optionGroups.Find(new Func <OptionGroup, bool>(
                                           delegate(OptionGroup searchGroup)
                {
                    return(attribute.GroupId.Equals(searchGroup.Id));
                }), out mGroup))
                {
                    throw new LogicException(String.Format(CultureInfo.CurrentUICulture, "Undefined group {0} referenced from  member {1} in {2}", attribute.GroupId, memberInfo.Name, cmdLineObject.GetType().FullName));
                }
                mGroup.Options.Add(mName, this);
            }

            // Recursively find out if this option requires explicit assignment
            if (attribute.DoesRequireExplicitAssignment.HasValue)
            {
                mRequireExplicitAssignment = attribute.DoesRequireExplicitAssignment.Value;
            }
            else if (mGroup != null)
            {
                mRequireExplicitAssignment = mGroup.RequireExplicitAssignment;
            }
            else
            {
                mRequireExplicitAssignment = objectAttr.RequireExplicitAssignment;
            }

            // Make sure the type of the field, property or method is supported
            if (!IsTypeSupported(mOptionType))
            {
                throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                             "Unsupported type for command line option.");
            }


            // Make sure MinValue and MaxValue is not specified for any non-numerical type.
            if (mMinValue != null || mMaxValue != null)
            {
                if (!IsNumericalType)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                 "MinValue and MaxValue must not be specified for a non-numerical type");
                }
                else if (!mMinValue.GetType().IsAssignableFrom(GetBaseType(mOptionType)))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                 "Illegal value for MinValue or MaxValue, not the same type as option");
                }
            }

            // Some special checks for numerical types
            if (IsNumericalType)
            {
                // Assign the default MinValue if it was not set and this is a numerical type
                if (IsNumericalType && mMinValue == null)
                {
                    mMinValue = GetBaseType(mOptionType).GetField("MinValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
                }

                // Assign the defaul MaxValue if it was not set and this is a numerical type
                if (IsNumericalType && mMaxValue == null)
                {
                    mMaxValue = GetBaseType(mOptionType).GetField("MaxValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
                }

                // Check that MinValue <= MaxValue
                if (IsNumericalType && ((IComparable)MinValue).CompareTo(MaxValue) > 0)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                 "MinValue must not be greater than MaxValue");
                }
            }

            // Check that the DefaultValue is not set if the option does not require explicit assignment.
            // If it were allowed, it would be ambiguos for an option separated from a value by a white space character
            // since we wouldn't know whether that would set the default value or assign it to the following value.
            if (mDefaultValue != null && !mRequireExplicitAssignment)
            {
                throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                             "DefaultValue must not be specified when RequireExplicitAssignment is set to false");
            }

            // Check that the type of any set default value matches that of this option, or is string, and
            // convert it to the type of this option.
            if (mDefaultValue != null)
            {
                if (mDefaultValue.GetType() == typeof(string))
                {
                    try
                    {
                        mDefaultValue = GetCheckedValueForSetOperation(mDefaultValue);
                    }
                    catch (OverflowException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                     "DefaultValue was less than MinValue or greater than MaxValue for this option");
                    }
                    catch (FormatException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                     "DefaultValue was not specified in the correct format for the type of this option");
                    }
                }
                else if (GetBaseType(mOptionType) != mDefaultValue.GetType())
                {
                    try
                    {
                        mDefaultValue = Convert.ChangeType(mDefaultValue, GetBaseType(mOptionType), mNumberFormatInfo);
                    }
                    catch (InvalidCastException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                     "The type of the DefaultValue specified is not compatible with the type of the member to which this attribute applies");
                    }
                }
            }

            // If this is an enum, check that it doesn't have members only distinguishable by case, and
            // add the members to the mEnumerationValues set for speedy access when checking values assigned
            // to this member.
            Type type = GetBaseType(mOptionType);

            if (type.IsEnum)
            {
                mEnumerationValues = new TreeSet <string>(StringComparer.OrdinalIgnoreCase);
                foreach (FieldInfo field in type.GetFields())
                {
                    if (field.IsLiteral)
                    {
                        if (mEnumerationValues.Contains(field.Name))
                        {
                            throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                                         "This enumeration is not allowed as a command line option since it contains fields that differ only by case");
                        }
                        mEnumerationValues.Add(field.Name);
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Option"/> class.
        /// </summary>
        /// <param name="attribute">The attribute describing this option.</param>
        /// <param name="memberInfo">The <see cref="MemberInfo"/> object pointing to the member to which the attribute was applied.</param>
        /// <param name="cmdLineObject">The command line manager object.</param>
        /// <param name="optionGroups">A complete list of all available option groups.</param>
        /// <param name="numberFormatInfo">The number format info to use for parsing numerical arguments.</param>
        public Option(CommandLineOptionAttribute attribute, MemberInfo memberInfo, object cmdLineObject, 
            ICollection<OptionGroup> optionGroups, NumberFormatInfo numberFormatInfo)
        {            
            mObject = cmdLineObject;
            mMember = memberInfo;
            mUsage = attribute.BoolFunction;
            mDescription = attribute.Description;
            mNumberFormatInfo = numberFormatInfo ?? CultureInfo.CurrentCulture.NumberFormat;
            mDefaultValue = attribute.DefaultAssignmentValue;
            mMinValue = attribute.MinValue;
            mMaxValue = attribute.MaxValue;

            // Check the validity of the member for which this attribute was defined
            switch (memberInfo.MemberType)
            {
                case MemberTypes.Field:
                    FieldInfo fieldInfo = (FieldInfo)memberInfo;
                    if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal field for this attribute; field must be writeable");

                    mOptionType = fieldInfo.FieldType;
                    break;
                case MemberTypes.Method:
                    MethodInfo method = (MethodInfo)memberInfo;
                    ParameterInfo[] parameters = method.GetParameters();

                    if (parameters.Length != 1)
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal method for this attribute; the method must accept exactly one parameter");

                    if (parameters[0].IsOut)
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal method for this attribute; the parameter of the method must not be an out parameter");

                    if (IsArray(parameters[0].ParameterType) || IsCollectionType(parameters[0].ParameterType))
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal method for this attribute; the parameter of the method must be a non-array and non-collection type");

                    mOptionType = parameters[0].ParameterType;
                    break;
                case MemberTypes.Property:
                    PropertyInfo propInfo = (PropertyInfo)memberInfo;

                    if (!propInfo.CanWrite && !IsCollectionType(propInfo.PropertyType))
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal property for this attribute; property for non-collection type must be writable");

                    if (!propInfo.CanRead && IsCollectionType(propInfo.PropertyType))
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal property for this attribute; property for collection type must be readable");

                    if (!(propInfo.CanRead && propInfo.CanWrite) && IsArray(propInfo.PropertyType))
                        throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                            "Illegal property for this attribute; property representing array type must be both readable and writeable");

                    mOptionType = propInfo.PropertyType;
                    break;
                default:
                    throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                        "Illegal member for this attribute; member must be a property, method (accepting one parameter) or a field");
            }

            mMinOccurs = attribute.MinOccurs;

            // MaxOccurs does not have a default value (since this is different for various types), so we set it here.
            if (!attribute.IsMaxOccursSet)
            {
                // Use default setting for MaxOccurs
                if (IsArray(mOptionType) || IsCollectionType(mOptionType))
                    mMaxOccurs = 0; // Unlimited 
                else
                    mMaxOccurs = 1;
            }
            else
            {
                mMaxOccurs = attribute.MaxOccurs;
            }

            if (mMinOccurs > mMaxOccurs && mMaxOccurs > 0)
                throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo, 
                    String.Format(CultureInfo.CurrentUICulture, "MinOccurs ({0}) must not be larger than MaxOccurs ({1})", mMinOccurs, mMaxOccurs));

            if (mMaxOccurs != 1 && !(IsArray(mOptionType) || IsCollectionType(mOptionType)) && mMember.MemberType != MemberTypes.Method)
                throw new AttributeException(typeof(CommandLineOptionAttribute), memberInfo,
                    "Invalid cardinality for member; MaxOccurs must be equal to one (1) for any non-array or non-collection type");

            CommandLineManagerAttribute objectAttr = (CommandLineManagerAttribute)Attribute.GetCustomAttribute(mObject.GetType(), typeof(CommandLineManagerAttribute));
            if (objectAttr == null)
                throw new AttributeException(String.Format(CultureInfo.CurrentUICulture, 
                    "Class {0} contains a CommandLineOptionAttribute, but does not have the attribute CommandLineObjectAttribute", mObject.GetType().FullName));


            // Assign the name of this option from the member itself if no name is explicitly provided 
            if (attribute.Name == null)
            {
                mName = memberInfo.Name;
            }
            else
            {
                mName = attribute.Name;
            }

            // Find the group (if any) that this option belongs to in the list of available option groups
            if (attribute.GroupId != null)
            {
                if (!optionGroups.Find(new Fun<OptionGroup, bool>(
                    delegate(OptionGroup searchGroup)
                    {
                        return attribute.GroupId.Equals(searchGroup.Id);
                    }), out mGroup)) 
                {
                    throw new LogicException(String.Format(CultureInfo.CurrentUICulture, "Undefined group {0} referenced from  member {1} in {2}", attribute.GroupId, memberInfo.Name, cmdLineObject.GetType().FullName));
                }
                mGroup.Options.Add(mName, this);
            }

            // Recursively find out if this option requires explicit assignment
            if (attribute.DoesRequireExplicitAssignment.HasValue)
            {
                mRequireExplicitAssignment = attribute.DoesRequireExplicitAssignment.Value;
            }
            else if (mGroup != null)
            {
                mRequireExplicitAssignment = mGroup.RequireExplicitAssignment;
            }
            else
            {
                mRequireExplicitAssignment = objectAttr.RequireExplicitAssignment;
            }

            // Make sure the type of the field, property or method is supported
            if (!IsTypeSupported(mOptionType))
                throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                    "Unsupported type for command line option.");


            // Make sure MinValue and MaxValue is not specified for any non-numerical type.
            if (mMinValue != null || mMaxValue != null)
            {
                if (!IsNumericalType)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                        "MinValue and MaxValue must not be specified for a non-numerical type");
                }
                else if (!mMinValue.GetType().IsAssignableFrom(GetBaseType(mOptionType)))
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                        "Illegal value for MinValue or MaxValue, not the same type as option");
                }
            }

            // Some special checks for numerical types
            if (IsNumericalType)
            {
                // Assign the default MinValue if it was not set and this is a numerical type
                if (IsNumericalType && mMinValue == null)
                {
                    mMinValue = GetBaseType(mOptionType).GetField("MinValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
                }

                // Assign the defaul MaxValue if it was not set and this is a numerical type
                if (IsNumericalType && mMaxValue == null)
                {
                    mMaxValue = GetBaseType(mOptionType).GetField("MaxValue", BindingFlags.Static | BindingFlags.Public).GetValue(null);
                }

                // Check that MinValue <= MaxValue
                if (IsNumericalType && ((IComparable)MinValue).CompareTo(MaxValue) > 0)
                {
                    throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                        "MinValue must not be greater than MaxValue");
                }
            }

            // Check that the DefaultValue is not set if the option does not require explicit assignment. 
            // If it were allowed, it would be ambiguos for an option separated from a value by a white space character
            // since we wouldn't know whether that would set the default value or assign it to the following value.
            if (mDefaultValue != null && !mRequireExplicitAssignment)
            {
                throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                    "DefaultValue must not be specified when RequireExplicitAssignment is set to false");
            }

            // Check that the type of any set default value matches that of this option, or is string, and
            // convert it to the type of this option.
            if (mDefaultValue != null)
            {
                if (mDefaultValue.GetType() == typeof(string))
                {
                    try
                    {
                        mDefaultValue = GetCheckedValueForSetOperation(mDefaultValue);
                    }
                    catch (OverflowException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                            "DefaultValue was less than MinValue or greater than MaxValue for this option");
                    }
                    catch (FormatException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                            "DefaultValue was not specified in the correct format for the type of this option");
                    }
                }
                else if (GetBaseType(mOptionType) != mDefaultValue.GetType())
                {
                    try
                    {
                        mDefaultValue = Convert.ChangeType(mDefaultValue, GetBaseType(mOptionType), mNumberFormatInfo);
                    }
                    catch (InvalidCastException)
                    {
                        throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                            "The type of the DefaultValue specified is not compatible with the type of the member to which this attribute applies");
                    }
                }
            }            

            // If this is an enum, check that it doesn't have members only distinguishable by case, and
            // add the members to the mEnumerationValues set for speedy access when checking values assigned 
            // to this member.
            Type type = GetBaseType(mOptionType);
            if (type.IsEnum)
            {
                mEnumerationValues = new TreeSet<string>(StringComparer.OrdinalIgnoreCase);
                foreach (FieldInfo field in type.GetFields())
                {
                    if (field.IsLiteral)
                    {
                        if (mEnumerationValues.Contains(field.Name))
                        {
                            throw new AttributeException(typeof(CommandLineOptionAttribute), mMember,
                                "This enumeration is not allowed as a command line option since it contains fields that differ only by case");
                        }
                        mEnumerationValues.Add(field.Name);
                    }
                }                
            }
        }