private bool CopyConfigDeclarationsRecursive(
SectionUpdates declarationUpdates, XmlUtil xmlUtil, XmlUtilWriter utilWriter, 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 section declarations that apply to this group
//
if (declarationUpdates != null) {
string[] movedSectionNames = declarationUpdates.GetMovedSectionNames();
if (movedSectionNames != null) {
if (!utilWriter.IsLastLineBlank) {
utilWriter.AppendNewLine();
}
foreach (string configKey in movedSectionNames) {
DeclarationUpdate sectionUpdate = declarationUpdates.GetDeclarationUpdate(configKey);
Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section");
// Write the one line section declaration.
utilWriter.AppendSpacesToLinePosition(linePosition);
utilWriter.Write(sectionUpdate.UpdatedXml);
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 group hierarchy element.
// - Write the element verbatim because it is unchanged
// - 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;
DeclarationUpdate sectionUpdate = null;
DeclarationUpdate groupUpdate = null;
SectionUpdates declarationUpdatesChild = null;
SectionUpdates recurseDeclarationUpdates = declarationUpdates;
string recurseGroup = group;
// update the lineposition and indent for each element
indent = UpdateIndent(indent, xmlUtil, utilWriter, parentLinePosition);
linePosition = xmlUtil.TrueLinePosition;
string directive = reader.Name;
string name = reader.GetAttribute(KEYWORD_SECTIONGROUP_NAME);
string configKey = CombineConfigKey(group, name);
if (directive == KEYWORD_SECTIONGROUP) {
// it's a group - get the updates for children
declarationUpdatesChild = declarationUpdates.GetSectionUpdatesForGroup(name);
if (declarationUpdatesChild != null) {
// get the group update
groupUpdate = declarationUpdatesChild.GetSectionGroupUpdate();
// recurse if there are more sections to copy
if (declarationUpdatesChild.HasUnretrievedSections()) {
recurse = true;
recurseGroup = configKey;
recurseDeclarationUpdates = declarationUpdatesChild;
}
}
}
else {
// it is a section - get the update
Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section");
sectionUpdate = declarationUpdates.GetDeclarationUpdate(configKey);
}
bool writeGroupUpdate = (groupUpdate != null && groupUpdate.UpdatedXml != null);
if (recurse) {
#if DBG
string startElementName = reader.Name;
#endif
// create a checkpoint that we can revert to if no children are written
object checkpoint = utilWriter.CreateStreamCheckpoint();
string closingElement = null;
// Copy this element node and up to the first subelement
if (writeGroupUpdate) {
// replace the element with the updated xml
utilWriter.Write(groupUpdate.UpdatedXml);
// skip over the start element
reader.Read();
}
else {
closingElement= xmlUtil.UpdateStartElement(utilWriter, null, true, linePosition, indent);
}
if (closingElement == null) {
// Only if there is a closing element should
// we move to it
xmlUtil.CopyReaderToNextElement(utilWriter, true);
}
// Recurse
bool recurseWroteASection = CopyConfigDeclarationsRecursive(
recurseDeclarationUpdates, xmlUtil, utilWriter, recurseGroup, linePosition, indent);
if (closingElement != null) {
utilWriter.AppendSpacesToLinePosition(linePosition);
utilWriter.Write(closingElement);
// Since we already got to </configSections> in reader, lets
// indent so we can copy the element in the right place
utilWriter.AppendSpacesToLinePosition(parentLinePosition);
} else {
// Copy the end element
xmlUtil.CopyXmlNode(utilWriter);
}
if (recurseWroteASection || writeGroupUpdate) {
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;
bool skipChildElements = false;
if (sectionUpdate == null) {
skip = true;
if (writeGroupUpdate) {
// Insert an empty <sectionGroup type="typename" > node, to introduce the type
wroteASection = true;
utilWriter.Write(groupUpdate.UpdatedXml);
utilWriter.AppendNewLine();
utilWriter.AppendSpacesToLinePosition(linePosition);
utilWriter.Write(FORMAT_SECTIONGROUP_ENDELEMENT);
utilWriter.AppendNewLine();
utilWriter.AppendSpacesToLinePosition(linePosition);
}
else if (groupUpdate != null) {
// VSWhidbey 522450
// If groupUpdate exists, that means we've decided in GetConfigDeclarationUpdates
// that the section group should stay in the file.
Debug.Assert(groupUpdate.UpdatedXml == null, "groupUpdate.UpdatedXml == null");
Debug.Assert(!declarationUpdatesChild.HasUnretrievedSections(),
"If the group has any unretrieved section, we should have chosen the recursive code path above.");
wroteASection = true;
skip = false;
// We should skip all the child sections. If we indeed need to keep any child
// section, we should have chosen the recursive code path above.
skipChildElements = true;
}
}
else {
wroteASection = true;
if (sectionUpdate.UpdatedXml == null) {
skip = false;
}
else {
skip = true;
// Write the updated XML on a single line
utilWriter.Write(sectionUpdate.UpdatedXml);
}
}
if (skip) {
//
// Skip over the existing element, then
// copy up to the next element, or exit this level.
//
xmlUtil.SkipAndCopyReaderToNextElement(utilWriter, true);
}
else {
if (skipChildElements) {
xmlUtil.SkipChildElementsAndCopyOuterXmlToNextElement(utilWriter);
}
else {
// Copy this entire contents of this element and then to the next element, or exit this level.
xmlUtil.CopyOuterXmlToNextElement(utilWriter, true);
}
}
}
}
}
return wroteASection;
}