/// <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); } } } }
/// <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); } } } }