protected MemberInfoUsage[] GetMembers() { // Return cached members if (members != null) { return(members); } // Cache members ArrayList memberList = new ArrayList(); // TODO: Parse base class first to get /v and /h shown first BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; foreach (MemberInfo info in GetType().GetMembers(flags)) { // Only doing fields and properties if ((info.MemberType != MemberTypes.Field) && (info.MemberType != MemberTypes.Property)) { continue; } // Skip variables w/o usage object[] attribs = info.GetCustomAttributes(typeof(UsageAttribute), true); UsageAttribute usage = (UsageAttribute)(attribs.Length != 0 ? attribs[0] : null); if (usage is NoUsageAttribute) { continue; } // Default settings with no attribute if (usage == null) { PropertyInfo prop = info as PropertyInfo; FieldInfo field = info as FieldInfo; Debug.Assert((prop != null) || (field != null)); // If the type is bool, it's probably just a flag if (((prop != null) && (prop.PropertyType == typeof(bool))) || ((field != null) && (field.FieldType == typeof(bool)))) { usage = new FlagUsageAttribute(info.Name); } // If the type is not a bool, it probably also have a value else { usage = new ValueUsageAttribute(info.Name); } } memberList.Add(new MemberInfoUsage(info, usage)); } members = (MemberInfoUsage[])memberList.ToArray(typeof(MemberInfoUsage)); return(members); }
protected MemberInfoUsage[] GetMembers() { // Return cached members if( members != null ) return members; // Cache members ArrayList memberList = new ArrayList(); // TODO: Parse base class first to get /v and /h shown first BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; foreach( MemberInfo info in GetType().GetMembers(flags) ) { // Only doing fields and properties if( (info.MemberType != MemberTypes.Field) && (info.MemberType != MemberTypes.Property) ) continue; // Skip variables w/o usage object[] attribs = info.GetCustomAttributes(typeof(UsageAttribute), true); UsageAttribute usage = (UsageAttribute)(attribs.Length != 0 ? attribs[0] : null); if( usage is NoUsageAttribute ) continue; // Default settings with no attribute if( usage == null ) { PropertyInfo prop = info as PropertyInfo; FieldInfo field = info as FieldInfo; Debug.Assert((prop != null) || (field != null)); // If the type is bool, it's probably just a flag if( ((prop != null) && (prop.PropertyType == typeof(bool))) || ((field != null) && (field.FieldType == typeof(bool))) ) { usage = new FlagUsageAttribute(info.Name); } // If the type is not a bool, it probably also have a value else { usage = new ValueUsageAttribute(info.Name); } } memberList.Add(new MemberInfoUsage(info, usage)); } members = (MemberInfoUsage[])memberList.ToArray(typeof(MemberInfoUsage)); return members; }
void Parse(string[] args, bool ignoreFirstArg) { object[] attribs = GetType().GetCustomAttributes(typeof(ParserUsageAttribute), true); ParserUsageAttribute parser = (ParserUsageAttribute)(attribs.Length != 0 ? attribs[0] : null); bool allowArgFile = (parser != null ? parser.AllowArgumentFile : true); MemberInfoUsage[] members = GetMembers(); for (int i = (ignoreFirstArg ? 1 : 0); i != args.Length; ++i) { string arg = args[i]; Debug.WriteLine("Processing arg: " + arg); MemberInfoUsage member = null; bool isFlag = false; // It's a flag if ((arg.Length > 1) && ((arg[0] == '/') || (arg[0] == '-'))) { bool hasOnOff; string flagName; // Flags can have a '+' or '-' suffix. If this arg has a prefix remove it, // else just remove the '/' or '-' hasOnOff = arg.EndsWith("-") || arg.EndsWith("+"); if (hasOnOff) { flagName = arg.Substring(1, arg.Length - 2); } else { flagName = arg.Substring(1); } // Flags can be passed in one of two ways // a) With a param name in 1 arg and a value in the next. e.g. "/flag myValue" // b) Using a delimiter, passed as a single argument. e.g. "/flag:myValue" // If using the single arg (a) method extract the arg name int delimiterPos = FindValueInlineDeliminter(flagName); //default delimiter. A space which is not a delimiter (space delimted values are split into seperate args) // will thus have a delimiter value of '\0' char delimiter = '\0'; if (delimiterPos != -1) { delimiter = flagName[delimiterPos]; flagName = flagName.Substring(0, delimiterPos); } // Find the argument by name member = FindFlag(flagName); if (member != null) { isFlag = true; //OnOff toggle only allowed if: member.usage is FlagUsage and FlagUsage.AllowOnOff is true if (hasOnOff) { FlagUsageAttribute flag = member.usage as FlagUsageAttribute; if (null == flag) { throw new UsageException(arg, "Only flags support on/off toggles"); } else if (!flag.AllowOnOff) { throw new UsageException(arg, "Flag does not allow on/off toggle"); } } //Check that only value args have a delimiter and that the correct delimiter has been used if (member.usage is ValueUsageAttribute) { ValueUsageAttribute val = (ValueUsageAttribute)member.usage; switch (delimiter) { case ':': if (0 == (val.Delimiters & ValueDelimiters.Colon)) { throw new UsageException(arg, "This value param does not support a ':' delimiter"); } break; case '=': if (0 == (val.Delimiters & ValueDelimiters.Equals)) { throw new UsageException(arg, "This value param does not support a '=' delimiter"); } break; case '\0': if (0 == (val.Delimiters & ValueDelimiters.Space)) { throw new UsageException(arg, "This value param does not support a ' ' delimiter"); } break; default: throw new UsageException(arg, "Unknown delimiter"); } } else if ('\0' != delimiter) { throw new UsageException(arg, "Only value params support delimiters"); } } } // It's a file name to process parameters from else if ((arg.Length > 1) && (arg[0] == '@') && allowArgFile) { ParseFromFile(arg.Substring(1)); continue; } // It's a parameter else { // Find the argument by offset member = GetNextParam(); } if (member == null) { throw new UsageException(arg, "Unrecognized argument"); } // Argument with a value, e.g. /foo bar if (member.usage.ExpectsValue) { string value; // If the arg has an inline delimiter (e.g. "/flag:myValue") // then read the value from the current arg else get the value from the next arg int delimiterPos = FindValueInlineDeliminter(arg); if (delimiterPos == -1) { if (isFlag && (++i == args.Length)) { throw new UsageException(arg, "Argument expects a parameter"); } value = args[i]; } else { if ((arg.Length - 1) == delimiterPos) { throw new UsageException(arg, "Argument expects a paramter"); } value = arg.Substring(delimiterPos + 1); } member.usage.ConsumeValue(value, this, member.info); } // Argument w/o a value, e.g. /foo else { string value; if (isFlag && arg.EndsWith("-")) { value = "false"; } else { value = "true"; } member.usage.ConsumeValue(value, this, member.info); } } // Check for missing required arguments foreach (MemberInfoUsage member in members) { if (!member.usage.Optional && !member.usage.IsFound) { throw new UsageException(member.info.Name, "Required argument not found"); } } }