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