/// <summary>Parse an XML configuration file matching this type.</summary>
        /// <param name="reader">An <see cref="XmlReader"/> which does the actual XML reading.</param>
        /// <param name="errors">[out] Returns a dictionary into which parse errors will be placed, where
        /// the key is the name of the argument the user provided, and the value is an error message
        /// describing that argument.</param>
        /// <returns>True if no errors were encountered, false otherwise.</returns>
        public bool ReadXml(XmlReader reader, out Dictionary<string, string> errors)
        {
            ConfigurationParseContext parseContext = new ConfigurationParseContext();
            if (this.ParseXmlConfigFileRaw(reader, parseContext))
            {
                // Check for all required
                List<PropertyInfo> missing = _required.FindAll(x => !parseContext.FlagsSetInConfigurationFile.Contains(x) && ((_fields[x].Type & FieldTypes.CliOnly) == FieldTypes.None)); // it's only missing if it's required *and* it's not CLI only
                foreach (PropertyInfo f in missing)
                {
                    parseContext.AddConfigurationError(f.Name, "missing required element");
                }
            }

            errors = parseContext.GenerateErrorDictionary();
            return errors.Count == 0;
        }
        private bool ParseResponseFile(ConfigurationParseContext parseContext, string responseFileName)
        {
            string commandText;
            try
            {
                commandText = File.ReadAllText(responseFileName);
            }
            catch (IOException ex)
            {
                parseContext.AddCommandLineError(string.Empty, "While loading command line response file " + responseFileName + ": " + ex.Message);
                return false;
            }

            return this.ParseArgsImpl(null, ArgumentSplitter.CommandLineToArgvW(commandText), false, parseContext);
        }
        private bool ParseArgsImpl(string defaultConfig, IList<string> args, bool allowConfigFileLoad, ConfigurationParseContext parseContext)
        {
            PropertyInfo lastArg = null;
            bool status = true;

            if (defaultConfig != null)
            {
                if (!ParseXmlConfigFileSafe(parseContext, defaultConfig))
                {
                    status = false;
                }
            }

            bool lastArgWasUnknown = false;
            for (int idx = 0; idx < args.Count;)
            {
                string arg = args[idx];

                if (arg.StartsWith("@", StringComparison.Ordinal) && allowConfigFileLoad)
                {
                    string configFileName = arg.Substring(1);
                    if (configFileName.EndsWith(".rsp", StringComparison.OrdinalIgnoreCase))
                    {
                        status = status && ParseResponseFile(parseContext, configFileName);
                    }
                    else
                    {
                        status = status && ParseXmlConfigFileSafe(parseContext, configFileName);
                    }

                    idx++;
                    lastArg = null;
                    lastArgWasUnknown = false;
                }
                else if (arg.Equals("/?", StringComparison.Ordinal) || arg.Equals("-?", StringComparison.Ordinal) || arg.Equals("--?", StringComparison.Ordinal))
                {
                    this.PrintUsage();
                    if (allowConfigFileLoad)
                    {
                        Console.Out.WriteLine("Invoke with @file.rsp (with .rsp extension) to load console response file.");
                        Console.Out.WriteLine("Invoke with @file.xml (with anything but .rsp extension) to load XML config.");
                    }

                    parseContext.RemoveAllErrors();
                    return false;
                }
                else if (arg.StartsWith("/", StringComparison.Ordinal) || arg.StartsWith("-", StringComparison.Ordinal))
                {
                    // parse arg value
                    string name = arg.Substring(1);
                    if (_names.TryGetValue(name, out lastArg))
                    {
                    }
                    else if (_shortNames.TryGetValue(name, out lastArg))
                    {
                        name = _fields[lastArg].Name;
                    }
                    else if (name.EndsWith("-", StringComparison.Ordinal))
                    {
                        name = name.Substring(0, name.Length - 1);
                        if (this.TryFindArgumentByName(name, out lastArg))
                        {
                            lastArgWasUnknown = false;
                            Type normalizedType = GetNormalizedType(lastArg.PropertyType);
                            if (normalizedType == typeof(bool) && HasNoMinusAttribute(lastArg))
                            {
                                parseContext.AddCommandLineError(name, "unknown argument (the negated version of this flag is explicitly disallowed)");
                                status = false;
                                lastArg = null;
                                idx++;
                            }
                            else if (normalizedType != typeof(bool))
                            {
                                parseContext.AddCommandLineError(name, "boolean switch used with non-boolean argument");
                                status = false;
                                lastArg = null;
                                idx++;
                            }
                        }
                        else
                        {
                            parseContext.AddCommandLineError(name, "unknown argument");
                            status = false;
                            lastArgWasUnknown = true;
                            idx++;
                        }
                    }
                    else
                    {
                        // error
                        parseContext.AddCommandLineError(name, "unknown argument");
                        status = false;
                        lastArgWasUnknown = true;
                        idx++;
                    }

                    if (lastArg != null && parseContext.FlagsSetOnCommandLine.Contains(lastArg) && (!IsArray(lastArg.PropertyType)))
                    {
                        parseContext.AddCommandLineError(name, "specified multiple times");
                        status = false;
                    }

                    if (lastArg != null && GetNormalizedType(lastArg.PropertyType) != typeof(bool))
                    {
                        lastArgWasUnknown = false;
                        idx++;
                    }
                }
                else if (_defaultField != null)
                {
                    lastArg = _defaultField;
                }
                else
                {
                    if (!lastArgWasUnknown)
                    {   // if the last arg was a typo, this probably isn't a separate error
                        parseContext.AddCommandLineError(string.Empty, "default argument given but there is no default argument");
                        status = false;
                    }

                    idx++; // try to keep parsing
                }

                if (lastArg != null)
                {
                    lastArgWasUnknown = false;
                    bool parseOK = true;

                    // We've seen the arg and set lastArg, so parse a value
                    if (parseContext.FlagsSetOnCommandLine.Contains(lastArg) && (!IsArray(lastArg.PropertyType)))
                    {
                        parseContext.AddCommandLineError(_fields[lastArg].Name, "specified multiple times");
                        parseOK = false;
                        status = false;
                    }

                    object value;
                    string parse_error;
                    int endIdx;
                    if (!this.ParseArg(lastArg.PropertyType, args, idx, out endIdx, out parse_error, out value))
                    {
                        parseContext.AddCommandLineError(_fields[lastArg].Name, parse_error);
                        parseOK = false;
                        status = false;
                    }

                    if (parseOK)
                    {
                        if (IsArray(lastArg.PropertyType))
                        {
                            // parse array
                            List<object> vals;

                            // add it to the list of arrays
                            if (parseContext.MultipleUseArgumentValues.TryGetValue(lastArg, out vals))
                            { // update the list of arrays
                                parseContext.MultipleUseArgumentValues[lastArg].Add(value);
                            }
                            else
                            {
                                vals = new List<object>();
                                vals.Add(value);
                                parseContext.MultipleUseArgumentValues.Add(lastArg, vals);
                                parseContext.FlagsSetOnCommandLine.Add(lastArg);
                            }
                        }
                        else
                        {
                            try
                            {
                                lastArg.SetValue(this, value, null);

                                // Remove any errors about this field from the config XML
                                parseContext.RemoveConfigurationError(_fields[lastArg].Name);
                                parseContext.FlagsSetOnCommandLine.Add(lastArg);
                            }
                            catch (TargetInvocationException ex)
                            {
                                if (ex.InnerException is ArgumentException)
                                {
                                    parseContext.AddCommandLineError(_fields[lastArg].Name, ex.InnerException.Message);
                                    status = false;
                                }
                                else
                                {
                                    throw;
                                }
                            }
                        }
                    }

                    idx = endIdx;
                    lastArg = null;
                }
            }

            List<PropertyInfo> missing = _required.FindAll(x => !parseContext.FlagsSetOnCommandLine.Contains(x) && !parseContext.FlagsSetInConfigurationFile.Contains(x));
            if (missing.Count > 0)
            {
                foreach (PropertyInfo f in missing)
                {
                    string n = f.Name;

                    // get the name of the arg, if it differs from the property name
                    foreach (KeyValuePair<string, PropertyInfo> v in _names)
                    {
                        if (v.Value.Name == f.Name)
                        {
                            n = v.Key;
                        }
                    }

                    parseContext.AddCommandLineError(n, "missing required argument");
                }

                status = false;
            }

            return status;
        }
        /// <summary>Parses the argument list.</summary>
        /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
        /// <exception cref="TargetInvocationException">Thrown when a target invocation error condition
        /// occurs.</exception>
        /// <param name="defaultConfig">The default configuration file (this value can be null)</param>
        /// <param name="args">The array of arguments, similar to <c>argv</c>.</param>
        /// <param name="allowConfigFileLoad">If <c>true</c>, allows the parser to load data from XML
        /// configuration files and console response files; otherwise disallows these actions.</param>
        /// <param name="errors">[out] Returns a dictionary into which parse errors will be placed, where
        /// the key is the name of the argument the user provided, and the value is an error message
        /// describing that argument.</param>
        /// <returns>
        /// <c>true</c> if a program should continue running after this method returns, or <c>false</c>
        /// if the program should terminate. (For example, returns <c>true</c> when the parse is
        /// successful, <c>false</c> if an erroneous argument is provided, and <c>false</c> if the user
        /// requested usage with /?)
        /// </returns>
        public bool ParseArgs(string defaultConfig, IList<string> args, bool allowConfigFileLoad, out Dictionary<string, string> errors)
        {
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }

            ConfigurationParseContext parseContext = new ConfigurationParseContext();
            bool status = ParseArgsImpl(defaultConfig, args, allowConfigFileLoad, parseContext);

            // Set arrays to their parsed values
            foreach (KeyValuePair<PropertyInfo, List<object>> arrayArgumentValue in parseContext.MultipleUseArgumentValues)
            {
                List<object> o = arrayArgumentValue.Value;
                Array a = Array.CreateInstance(arrayArgumentValue.Key.PropertyType.GetElementType(), o.Count);
                for (int i = 0; i < o.Count; i++)
                {
                    a.SetValue(o[i], i);
                }

                try
                {
                    arrayArgumentValue.Key.SetValue(this, a, null);
                }
                catch (TargetInvocationException ex)
                {
                    if (ex.InnerException is ArgumentException)
                    {
                        parseContext.AddCommandLineError(arrayArgumentValue.Key.Name, ex.InnerException.Message);
                        status = false;
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            errors = parseContext.GenerateErrorDictionary();
            if (errors.Count != 0)
            {
                status = false;
            }

            // Validate is only run if we parsed OK
            if (status)
            {
                return this.Validate(errors);
            }

            return status;
        }
        /// <summary>
        /// Parse an XML configuration file matching this type. Does NOT check for missing required
        /// arguments (that is done by ReadXml(XmlReader, out string)) or call Validate() method. It
        /// *only* throws setter errors.
        /// </summary>
        /// <exception cref="ArgumentNullException">Thrown when one or more required arguments are null.</exception>
        /// <exception cref="TargetInvocationException">Thrown when a target invocation error condition
        /// occurs.</exception>
        /// <param name="reader">An <see cref="XmlReader"/> which does the actual XML reading.</param>
        /// <param name="parseContext">Context for the configuration parse currently running.</param>
        /// <returns>True if no errors were encountered, false otherwise.</returns>
        private bool ParseXmlConfigFileRaw(XmlReader reader, ConfigurationParseContext parseContext)
        {
            List<PropertyInfo> mySetArgs = new List<PropertyInfo>();
            bool status = true;
            StringBuilder general_errors = new StringBuilder();
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            XmlDocument document = new XmlDocument();
            try
            {
                document.Load(reader);
            }
            catch (XmlException ex)
            {
                parseContext.AddConfigurationError(string.Empty, ex.Message);
                return false;
            }
            catch (UnauthorizedAccessException ex)
            {
                parseContext.AddConfigurationError(string.Empty, ex.Message);
                return false;
            }

            XmlElement root = null;
            foreach (XmlNode n in document.ChildNodes)
            {
                XmlElement asElement = n as XmlElement;
                if (asElement != null)
                {
                    if (n.Name == this.GetClassName())
                    {
                        root = asElement;
                    }
                    else
                    {
                        general_errors.AppendLine("unknown element " + n.Name);
                        status = false;
                    }
                }
            }

            if (root == null)
            {
                general_errors.AppendLine("missing root element " + this.GetClassName());
                status = false;
            }
            else
            {
                // TODO: should we check version here?
                XmlElement el;
                foreach (XmlNode node in root.ChildNodes)
                {
                    el = node as XmlElement;
                    if (el != null)
                    {
                        string name = el.Name;
                        PropertyInfo field;
                        if (_names.TryGetValue(name, out field))
                        {
                        }
                        else if (_shortNames.TryGetValue(name, out field))
                        {
                        }
                        else
                        {
                            parseContext.AddConfigurationError(name, "unrecognized element");
                            status = false;
                            continue;
                        }

                        if ((_fields[field].Type & FieldTypes.CliOnly) != FieldTypes.None)
                        {
                            parseContext.AddConfigurationError(name, "parameter can only be specified at the command line");
                            status = false;
                        }

                        if (mySetArgs.Contains(field) && !IsArray(field.PropertyType))
                        {
                            parseContext.AddConfigurationError(name, "specified multiple times");
                            status = false;
                        }

                        object value;
                        string e;
                        if (!this.ReadType(field.PropertyType, el, out value, out e))
                        {
                            parseContext.AddConfigurationError(field.Name, e);
                            status = false;
                        }

                        // don't set it if it was set by the CLI, for non-arrays
                        if (IsArray(field.PropertyType))
                        {
                            List<object> l;
                            if (parseContext.MultipleUseArgumentValues.TryGetValue(field, out l))
                            {
                                l.Add(value);
                            }
                            else
                            {
                                parseContext.MultipleUseArgumentValues.Add(field, new List<object>(new object[] { value }));
                            }
                        }
                        else
                        {
                            if (!parseContext.FlagsSetOnCommandLine.Contains(field))
                            {
                                try
                                {
                                    field.SetValue(this, value, null); // is this cast OK?
                                }
                                catch (TargetInvocationException ex)
                                {
                                    if (ex.InnerException is ArgumentException)
                                    {
                                        parseContext.AddConfigurationError(field.Name, ex.InnerException.Message);
                                        status = false;
                                    }
                                    else
                                    {
                                        throw;
                                    }
                                }
                            }

                            mySetArgs.Add(field);
                        }
                    }
                }
            }

            // now set any arrays
            foreach (PropertyInfo key in parseContext.MultipleUseArgumentValues.Keys)
            {
                Array v = Array.CreateInstance(key.PropertyType.GetElementType(), parseContext.MultipleUseArgumentValues[key].Count);
                int i = 0;
                foreach (object o in parseContext.MultipleUseArgumentValues[key])
                {
                    v.SetValue(o, i);
                    i++;
                }

                try
                {
                    key.SetValue(this, v, null);
                }
                catch (TargetInvocationException ex)
                {
                    if (ex.InnerException is ArgumentException)
                    {
                        parseContext.AddConfigurationError(key.Name, ex.InnerException.Message);
                        status = false;
                    }
                    else
                    {
                        throw;
                    }
                }

                mySetArgs.Add(key);
            }

            foreach (PropertyInfo arg in mySetArgs)
            {
                parseContext.FlagsSetInConfigurationFile.Add(arg);
            }

            if (general_errors.Length > 0)
            {
                parseContext.AddConfigurationError(string.Empty, general_errors.ToString());
            }

            return status;
        }
        private bool ParseXmlConfigFileSafe(ConfigurationParseContext parseContext, string configFileName)
        {
            try
            {
                using (XmlTextReader r = new XmlTextReader(configFileName) { DtdProcessing = DtdProcessing.Ignore })
                {
                    if (!this.ParseXmlConfigFileRaw(r, parseContext))
                    {
                        return false;
                    }
                }
            }
            catch (IOException ex)
            {
                parseContext.AddCommandLineError(string.Empty, "While loading XML configuration file " + configFileName + ": " + ex.Message);
                return false;
            }
            catch (InvalidOperationException ex)
            {
                parseContext.AddCommandLineError(string.Empty, "While loading XML configuration file " + configFileName + ": " + ex.Message);
                return false;
            }
            catch (UriFormatException ex)
            {
                parseContext.AddCommandLineError(string.Empty, "While loading XML configuration file " + configFileName + ": " + ex.Message);
                return false;
            }

            return true;
        }