private bool CopyConfigDefinitionsRecursive(
ConfigDefinitionUpdates configDefinitionUpdates, XmlUtil xmlUtil, XmlUtilWriter utilWriter,
bool locationPathApplies, LocationUpdates locationUpdates, SectionUpdates sectionUpdates,
bool addNewSections, string group, int parentLinePosition, int parentIndent) {
bool wroteASection = false;
XmlTextReader reader = xmlUtil.Reader;
int linePosition;
int indent;
int startingLinePosition;
indent = UpdateIndent(parentIndent, xmlUtil, utilWriter, parentLinePosition);
if (reader.NodeType == XmlNodeType.Element) {
linePosition = xmlUtil.TrueLinePosition;
startingLinePosition = linePosition;
}
else if (reader.NodeType == XmlNodeType.EndElement) {
linePosition = parentLinePosition + indent;
if (utilWriter.IsLastLineBlank) {
startingLinePosition = xmlUtil.TrueLinePosition;
}
else {
startingLinePosition = parentLinePosition;
}
}
else {
linePosition = parentLinePosition + indent;
startingLinePosition = 0;
}
//
// Write any new sections that apply to this group
//
if (sectionUpdates != null && addNewSections) {
// Remove newness, so we won't write again
sectionUpdates.IsNew = false;
Debug.Assert(locationPathApplies, "locationPathApplies");
string[] movedSectionNames = sectionUpdates.GetMovedSectionNames();
if (movedSectionNames != null) {
if (!utilWriter.IsLastLineBlank) {
utilWriter.AppendNewLine();
}
utilWriter.AppendSpacesToLinePosition(linePosition);
bool skipFirstIndent = true;
foreach (string configKey in movedSectionNames) {
DefinitionUpdate update = sectionUpdates.GetDefinitionUpdate(configKey);
WriteSectionUpdate(utilWriter, update, linePosition, indent, skipFirstIndent);
skipFirstIndent = false;
utilWriter.AppendNewLine();
wroteASection = true;
}
// Restore the whitespace we used for the first element, which is either a start or an end element.
utilWriter.AppendSpacesToLinePosition(startingLinePosition);
}
}
if (reader.NodeType == XmlNodeType.Element) {
//
// For each element at this depth, either:
// - Write the element verbatim and recurse due to a location section or group hierarchy element.
// - Write the element verbatim because it is unchanged, or because the current location does
// not apply.
// - Write the updated XML for the section.
// - Skip it because the section has been removed.
//
int depth = reader.Depth;
while (reader.Depth == depth) {
bool recurse = false;
DefinitionUpdate update = null;
bool elementLocationPathApplies = locationPathApplies;
LocationUpdates recurseLocationUpdates = locationUpdates;
SectionUpdates recurseSectionUpdates = sectionUpdates;
bool recurseAddNewSections = addNewSections;
string recurseGroup = group;
bool removedSectionOrGroup = false;
// update the lineposition and indent for each element
indent = UpdateIndent(indent, xmlUtil, utilWriter, parentLinePosition);
linePosition = xmlUtil.TrueLinePosition;
string elementName = reader.Name;
if (elementName == KEYWORD_LOCATION) {
string locationSubPathAttribute = reader.GetAttribute(KEYWORD_LOCATION_PATH);
locationSubPathAttribute = NormalizeLocationSubPath(locationSubPathAttribute, xmlUtil);
elementLocationPathApplies = StringUtil.EqualsIgnoreCase(_locationSubPath, locationSubPathAttribute);
bool allowOverride = true;
bool inheritInChildApps = true;
if (elementLocationPathApplies) {
// Retrieve allowOverride and InheritInChildApps
string allowOverrideAttribute = reader.GetAttribute(KEYWORD_LOCATION_ALLOWOVERRIDE);
if (allowOverrideAttribute != null) {
allowOverride = Boolean.Parse(allowOverrideAttribute);
}
string inheritInChildAppsAttribute = reader.GetAttribute(KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS);
if (inheritInChildAppsAttribute != null) {
inheritInChildApps = Boolean.Parse(inheritInChildAppsAttribute);
}
// Flag that we already have one of these locations
configDefinitionUpdates.FlagLocationWritten();
}
if (reader.IsEmptyElement) {
if (elementLocationPathApplies &&
(configDefinitionUpdates.FindLocationUpdates(allowOverride,
inheritInChildApps) != null)) {
// If we are going to make updates here, then
// delete the one that is here (so we can update later)
elementLocationPathApplies = true;
}
else {
// If not lets leave it
elementLocationPathApplies = false;
}
}
else {
// recurse if this location applies to us
if (elementLocationPathApplies) {
if (configDefinitionUpdates != null) {
recurseLocationUpdates = configDefinitionUpdates.FindLocationUpdates(allowOverride, inheritInChildApps);
if (recurseLocationUpdates != null) {
recurse = true;
recurseSectionUpdates = recurseLocationUpdates.SectionUpdates;
// If this is <location path=".">, we don't want to add moved sections
// to it.
if (_locationSubPath == null && recurseLocationUpdates.IsDefault) {
recurseAddNewSections = false;
}
}
}
}
else {
// recurse if necessary to remove items in _removedSections and _removedGroups
if (HasRemovedSectionsOrGroups && !IsLocationConfig && Host.SupportsLocation) {
recurse = true;
recurseLocationUpdates = null;
recurseSectionUpdates = null;
recurseAddNewSections = false;
}
}
}
}
else {
string configKey = CombineConfigKey(group, elementName);
FactoryRecord factoryRecord = FindFactoryRecord(configKey, false);
if (factoryRecord == null) {
// The factory was deleted, so regardless of whether this is a
// section or sectionGroup, it can be skipped.
if (!elementLocationPathApplies && !IsLocationConfig) {
removedSectionOrGroup = true;
}
}
else if (factoryRecord.IsGroup) {
if (reader.IsEmptyElement) {
if (!elementLocationPathApplies && !IsLocationConfig) {
removedSectionOrGroup = true;
}
}
else {
// if the location path applies, recurse if there are updates
if (sectionUpdates != null) {
SectionUpdates sectionUpdatesChild = sectionUpdates.GetSectionUpdatesForGroup(elementName);
if (sectionUpdatesChild != null) {
recurse = true;
recurseGroup = configKey;
recurseSectionUpdates = sectionUpdatesChild;
}
}
else if (!elementLocationPathApplies && !IsLocationConfig) {
if (_removedSectionGroups != null && _removedSectionGroups.Contains(configKey)) {
removedSectionOrGroup = true;
}
else {
recurse = true;
recurseGroup = configKey;
recurseLocationUpdates = null;
recurseSectionUpdates = null;
recurseAddNewSections = false;
}
}
}
}
else {
// it is a section - get the update
if (sectionUpdates != null) {
update = sectionUpdates.GetDefinitionUpdate(configKey);
}
else if (!elementLocationPathApplies && !IsLocationConfig) {
if (_removedSections != null && _removedSections.Contains(configKey)) {
removedSectionOrGroup = true;
}
}
}
}
if (recurse) {
#if DBG
string startElementName = reader.Name;
#endif
// flush, and get length of underlying stream
object checkpoint = utilWriter.CreateStreamCheckpoint();
// Copy this element node and up to the first subelement
xmlUtil.CopyXmlNode(utilWriter);
xmlUtil.CopyReaderToNextElement(utilWriter, true);
// Recurse
bool recurseWroteASection = CopyConfigDefinitionsRecursive(
configDefinitionUpdates, xmlUtil, utilWriter, elementLocationPathApplies, recurseLocationUpdates, recurseSectionUpdates,
recurseAddNewSections, recurseGroup, linePosition, indent);
// Copy the end element
xmlUtil.CopyXmlNode(utilWriter);
if (recurseWroteASection) {
wroteASection = true;
}
else {
// back out the change
utilWriter.RestoreStreamCheckpoint(checkpoint);
}
// Copy up to the next element, or exit this level.
xmlUtil.CopyReaderToNextElement(utilWriter, true);
}
else {
bool skip;
if (update == null) {
// remove the section from the file if we're in the correct location,
// or if the section or group should be removed from all locations
skip = elementLocationPathApplies || removedSectionOrGroup;
}
else {
// replace the section if the xml for it has been updated
// if it is a configSource, don't write it unless the configSource parameters have changed
skip = false;
if (update.UpdatedXml != null) {
ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result;
if ( String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource) ||
configSection.SectionInformation.ConfigSourceModified) {
skip = true;
WriteSectionUpdate(utilWriter, update, linePosition, indent, true);
wroteASection = true;
}
}
}
if (skip) {
//
// Skip over the existing element, then
// copy up to the next element, or exit this level.
//
xmlUtil.SkipAndCopyReaderToNextElement(utilWriter, true);
}
else {
// Copy this entire contents of this element and then to the next element, or exit this level.
xmlUtil.CopyOuterXmlToNextElement(utilWriter, true);
wroteASection = true;
}
}
}
}
//
// Write new section groups
//
if (sectionUpdates != null && addNewSections && sectionUpdates.HasNewSectionGroups()) {
// Add whitespace to align us with the other elements in this group
linePosition = parentLinePosition + indent;
if (reader.NodeType == XmlNodeType.EndElement) {
if (utilWriter.IsLastLineBlank) {
startingLinePosition = xmlUtil.TrueLinePosition;
}
else {
startingLinePosition = parentLinePosition;
}
}
else {
startingLinePosition = 0;
}
utilWriter.AppendSpacesToLinePosition(linePosition);
bool wroteNewSection = WriteNewConfigDefinitionsRecursive(utilWriter, sectionUpdates, linePosition, indent, true);
if (wroteNewSection) {
wroteASection = true;
}
// Restore the whitespace of the end element
utilWriter.AppendSpacesToLinePosition(startingLinePosition);
}
return wroteASection;
}