VerifyRequiredAttribute() private method

private VerifyRequiredAttribute ( object o, string attrName, ExceptionAction action ) : bool
o object
attrName string
action ExceptionAction
return bool
        // Scans the <configSections> section of a configuration file.  The function is recursive
        // to traverse arbitrarily nested config groups.
        //
        //     <sectionGroup name="foo">
        //         <sectionGroup name="bar">
        //             <section name="fooBarSection" type="..." />
        //     ...
        //
        // Note: This function valiates that the factory record has not been
        //       declared before in a parent record. (it does not check
        //       current record, which allows you to update list)
        //
        private void ScanFactoriesRecursive(XmlUtil xmlUtil, string parentConfigKey, Hashtable factoryList) {

            // discard any accumulated local errors
            xmlUtil.SchemaErrors.ResetLocalErrors();

            int depth = xmlUtil.Reader.Depth;
            xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);

            while (xmlUtil.Reader.Depth == depth + 1) {
                bool    positionedAtNextElement = false;
                
                switch (xmlUtil.Reader.Name) {
                    //
                    // Handle <sectionGroup name="groupName" [type="typename"] />
                    //
                    case KEYWORD_SECTIONGROUP: {
                        string tagName = null;
                        string typeName = null;

                        int lineNumber = xmlUtil.Reader.LineNumber;
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            switch (xmlUtil.Reader.Name) {
                                case KEYWORD_SECTIONGROUP_NAME:
                                    tagName = xmlUtil.Reader.Value;
                                    VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
                                    break;

                                case KEYWORD_SECTIONGROUP_TYPE:
                                    xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
                                    break;

                                default:
                                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
                                    break;
                            }
                        }
                        xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element

                        if (!xmlUtil.VerifyRequiredAttribute(
                                tagName, 
                                KEYWORD_SECTIONGROUP_NAME,
                                ExceptionAction.NonSpecific)) {
                                        
                            //
                            // Without a name, we cannot continue parsing the sections and groups within.
                            // Skip the entire section.
                            //
                            xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
                            xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                        }
                        else {
                            string configKey = CombineConfigKey(parentConfigKey, tagName);

                            FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
                            if (factoryRecord != null) {
                                // Error: duplicate sectionGroup declaration
                                xmlUtil.SchemaErrors.AddError(
                                        new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
                                        ExceptionAction.Local);

                            } else {
                                FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
                                if (parentFactoryRecord != null) {
                                    configKey = parentFactoryRecord.ConfigKey;


                                    // make sure that an ancestor has not defined a section with the same name as the group
                                    if (    !parentFactoryRecord.IsGroup              ||
                                            !parentFactoryRecord.IsEquivalentSectionGroupFactory(Host, typeName)) {
                                         
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                }

                                if (parentFactoryRecord != null) {
                                    factoryRecord = parentFactoryRecord.CloneSectionGroup(typeName, xmlUtil.Filename, lineNumber);
                                }
                                else {
                                    factoryRecord = new FactoryRecord(configKey, parentConfigKey, tagName, typeName, xmlUtil.Filename, lineNumber);
                                }

                                factoryList[configKey] = factoryRecord;
                            }

                            // Add any errors we may have encountered
                            factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));

                            // continue recursive scan
                            ScanFactoriesRecursive(xmlUtil, configKey, factoryList);
                        }

                        continue;
                    }

                    case KEYWORD_SECTION: {
                        string tagName = null;
                        string typeName = null;
                        ConfigurationAllowDefinition    allowDefinition = ConfigurationAllowDefinition.Everywhere;
                        ConfigurationAllowExeDefinition allowExeDefinition = ConfigurationAllowExeDefinition.MachineToApplication;
                        bool allowLocation = true;
                        bool restartOnExternalChanges = true;
                        bool requirePermission = true;
                        bool gotType = false;

                        // parse section attributes
                        int lineNumber = xmlUtil.Reader.LineNumber;
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            switch (xmlUtil.Reader.Name) {
                                case KEYWORD_SECTION_NAME:
                                    tagName = xmlUtil.Reader.Value;
                                    VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
                                    break;

                                case KEYWORD_SECTION_TYPE:
                                    xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
                                    gotType = true;
                                    break;

                                case KEYWORD_SECTION_ALLOWLOCATION:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out allowLocation);
                                    break;

                                case KEYWORD_SECTION_ALLOWEXEDEFINITION:
                                    try {
                                        allowExeDefinition = AllowExeDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
                                    }
                                    catch (ConfigurationException ce) {
                                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                    }

                                    break;

                                case KEYWORD_SECTION_ALLOWDEFINITION:
                                    try {
                                        allowDefinition = AllowDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
                                    }
                                    catch (ConfigurationException ce) {
                                        xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
                                    }

                                    break;

                                case KEYWORD_SECTION_RESTARTONEXTERNALCHANGES:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out restartOnExternalChanges);

                                    break;

                                case KEYWORD_SECTION_REQUIREPERMISSION:
                                    xmlUtil.VerifyAndGetBooleanAttribute(
                                            ExceptionAction.Local, true, out requirePermission);

                                    break;

                                default:
                                    xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
                                    break;
                            }
                        }

                        xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element

                        if (!xmlUtil.VerifyRequiredAttribute(
                                tagName, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {

                            //
                            // Without a name, we cannot continue to create a factoryRecord.
                            //
                            xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
                        }
                        else {
                            // Verify that the Type attribute was present.
                            // Note that 'typeName' will be null if the attribute was present
                            // but specified as an empty string.
                            if (!gotType) {
                                xmlUtil.AddErrorRequiredAttribute(KEYWORD_SECTION_TYPE, ExceptionAction.Local);
                            }

                            string configKey = CombineConfigKey(parentConfigKey, tagName);

                            FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
                            if (factoryRecord != null) {
                                // Error: duplicate section declaration
                                xmlUtil.SchemaErrors.AddError(
                                        new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
                                        ExceptionAction.Local);
                            } else {
                                FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
                                if (parentFactoryRecord != null) {
                                    configKey = parentFactoryRecord.ConfigKey;

                                    // make sure that an ancestor has not defined a section with the same name as the group
                                    if (parentFactoryRecord.IsGroup) {
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                    else if (!parentFactoryRecord.IsEquivalentSectionFactory(Host, typeName, allowLocation, allowDefinition, allowExeDefinition, restartOnExternalChanges, requirePermission)) {
                                        xmlUtil.SchemaErrors.AddError(
                                                new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
                                                ExceptionAction.Local);

                                        parentFactoryRecord = null;
                                    }
                                }

                                if (parentFactoryRecord != null) {
                                    // Note - Clone will propagate the IsFromTrustedConfigRecord bit, 
                                    // which is what we want - if this record is a duplicate of an ancestor,
                                    // the ancestor may be from a trusted config record.
                                    factoryRecord = parentFactoryRecord.CloneSection(xmlUtil.Filename, lineNumber);
                                }
                                else {
                                    factoryRecord = new FactoryRecord(
                                            configKey, 
                                            parentConfigKey, 
                                            tagName, 
                                            typeName, 
                                            allowLocation, 
                                            allowDefinition, 
                                            allowExeDefinition, 
                                            restartOnExternalChanges, 
                                            requirePermission,
                                            _flags[IsTrusted],
                                            false,  // isUndeclared
                                            xmlUtil.Filename,
                                            lineNumber);
                                }

                                factoryList[configKey] = factoryRecord;
                            }

                            // Add any errors we may have encountered
                            factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
                        }
                    }
                    break;

                    case KEYWORD_REMOVE: {
                        string name = null;
                        int lineNumber = -1;

                        // parse attributes
                        while (xmlUtil.Reader.MoveToNextAttribute()) {
                            if (xmlUtil.Reader.Name != KEYWORD_SECTION_NAME) {
                                xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific);
                            }

                            name = xmlUtil.Reader.Value;
                            lineNumber = xmlUtil.Reader.LineNumber;
                        }
                        xmlUtil.Reader.MoveToElement();

                        if (xmlUtil.VerifyRequiredAttribute(
                                name, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {

                            VerifySectionName(name, xmlUtil, ExceptionAction.NonSpecific, false);
                        }
                    }
                    break;

                    case KEYWORD_CLEAR: {
                        xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific);
                    }
                    break;

                    default:
                        xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
                        xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
                        positionedAtNextElement = true;
                        break;
                }

                if (!positionedAtNextElement) {
                    // Need to read to next element, and check if an unrecognized child
                    // element is found.
                    xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
                    
                    // unrecognized children are not allowed in <configSections>
                    if (xmlUtil.Reader.Depth > depth + 1) {
                        xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
                    
                        // Lets try to backup to where we are suppose to be
                        while (xmlUtil.Reader.Depth > (depth + 1)) {
                            xmlUtil.ReadToNextElement();
                        }
                    }
                }
            }
        }