internal bool SkipChildElementsAndCopyOuterXmlToNextElement(XmlUtilWriter utilWriter) { bool isEmptyElement = this._reader.IsEmptyElement; int lineNumber = this._reader.LineNumber; this.CopyXmlNode(utilWriter); if (!isEmptyElement) { while (this._reader.NodeType != XmlNodeType.EndElement) { if (this._reader.NodeType == XmlNodeType.Element) { this._reader.Skip(); if (this._reader.NodeType == XmlNodeType.Whitespace) { this._reader.Skip(); } } else { this.CopyXmlNode(utilWriter); } } if (this._reader.LineNumber != lineNumber) { utilWriter.AppendSpacesToLinePosition(this.TrueLinePosition); } this.CopyXmlNode(utilWriter); } return(this.CopyReaderToNextElement(utilWriter, true)); }
// // Copy a configuration section to a string, and advance the reader. // internal string CopySection() { ResetCachedStringWriter(); // Preserve whitespace for sections for backcompat WhitespaceHandling originalHandling = _reader.WhitespaceHandling; _reader.WhitespaceHandling = WhitespaceHandling.All; // Create string writer to write to XmlUtilWriter utilWriter = new XmlUtilWriter(_cachedStringWriter, false); // Copy the element CopyElement(utilWriter); // Reset whitespace handling _reader.WhitespaceHandling = originalHandling; if ((originalHandling == WhitespaceHandling.None) && (Reader.NodeType == XmlNodeType.Whitespace)) { // If we were previously suppose to skip whitespace, and now we // are at it, then lets jump to the next item _reader.Read(); } utilWriter.Flush(); string s = ((StringWriter)utilWriter.Writer).ToString(); return(s); }
// // Copy an XML element, then continue copying until we've hit the next element // or exited this depth. // internal bool CopyOuterXmlToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { CopyElement(utilWriter); // Copy until reaching the next element, or if limitDepth == true until we've exited this depth. return(CopyReaderToNextElement(utilWriter, limitDepth)); }
internal bool CopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { int depth; bool flag = true; if (limitDepth) { if (this._reader.NodeType == XmlNodeType.EndElement) { return(true); } depth = this._reader.Depth; } else { depth = 0; } Label_0026: if ((this._reader.NodeType != XmlNodeType.Element) && (this._reader.Depth >= depth)) { flag = this.CopyXmlNode(utilWriter); if (flag) { goto Label_0026; } } return(flag); }
// Skip over the current element and copy until the next element. // This function removes the one blank line that would otherwise // be inserted by simply skipping and copying to the next element // in a situation like this: // // <!-- end of previous configSection --> // <configSectionToDelete> // <content /> // <moreContent /> // </configSectionToDelete> // <!-- end of configSectionToDelete --> // <nextConfigSection /> internal bool SkipAndCopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { Debug.Assert(Reader.NodeType == XmlNodeType.Element, "_reader.NodeType == XmlNodeType.Element"); // If the last line before the element is not blank, then we do not have to // remove the blank line. if (!utilWriter.IsLastLineBlank) { Reader.Skip(); return(CopyReaderToNextElement(utilWriter, limitDepth)); } // Set the depth if we limit copying to this depth int depth = limitDepth ? Reader.Depth : 0; // Skip over the element Reader.Skip(); int lineNumberOfEndElement = Reader.LineNumber; // Read until we hit a a non-whitespace node or reach the end while (!Reader.EOF) { if (Reader.NodeType != XmlNodeType.Whitespace) { // // If the next non-whitepace node is on another line, // seek back to the beginning of the current blank line, // skip a blank line of whitespace, and copy the remaining whitespace. // if (Reader.LineNumber > lineNumberOfEndElement) { utilWriter.SeekToLineStart(); utilWriter.AppendWhiteSpace(lineNumberOfEndElement + 1, 1, LineNumber, TrueLinePosition); } break; } Reader.Read(); } // Copy nodes until we've reached the desired depth, or until we hit an element. while (!Reader.EOF) { if (Reader.NodeType == XmlNodeType.Element) { break; } if (Reader.Depth < depth) { break; } CopyXmlNode(utilWriter); } return(!Reader.EOF); }
// // Copy or replace an element node. // If the element is an empty element, replace it with a formatted start element if either: // * The contents of the start element string need updating. // * The element needs to contain child elements. // // If the element is empty and is replaced with a start/end element pair, return a // end element string with whitespace formatting; otherwise return null. // internal string UpdateStartElement(XmlUtilWriter utilWriter, string updatedStartElement, bool needsChildren, int linePosition, int indent) { Debug.Assert(Reader.NodeType == XmlNodeType.Element, "_reader.NodeType == NodeType.Element"); string endElement = null; bool needsEndElement = false; string elementName = Reader.Name; // If the element is empty, determine if a new end element is needed. if (Reader.IsEmptyElement) { if ((updatedStartElement == null) && needsChildren) { updatedStartElement = RetrieveFullOpenElementTag(); } needsEndElement = updatedStartElement != null; } if (updatedStartElement == null) { // If no changes to the start element are required, just copy it. CopyXmlNode(utilWriter); } else { // Format a new start element/end element pair string updatedEndElement = "</" + elementName + ">"; string updatedElement = updatedStartElement + updatedEndElement; string formattedElement = FormatXmlElement(updatedElement, linePosition, indent, true); // Get the start and end element strings from the formatted element. int iEndElement = formattedElement.LastIndexOf('\n') + 1; string startElement; if (needsEndElement) { endElement = formattedElement.Substring(iEndElement); // Include a newline in the start element as we are expanding an empty element. startElement = formattedElement.Substring(0, iEndElement); } else { // Omit the newline from the start element. startElement = formattedElement.Substring(0, iEndElement - 2); } // Write the new start element. utilWriter.Write(startElement); // Skip over the existing start element. Reader.Read(); } return(endElement); }
// // Copy an XML element but skip all its child elements, then continue copying until we've hit the next element. // internal bool SkipChildElementsAndCopyOuterXmlToNextElement(XmlUtilWriter utilWriter) { bool isEmptyElement = _reader.IsEmptyElement; int startingLine = _reader.LineNumber; #if DBG int depth = _reader.Depth; #endif Debug.Assert(_reader.NodeType == XmlNodeType.Element, "_reader.NodeType == XmlNodeType.Element"); CopyXmlNode(utilWriter); // See if we need to skip any child element if (!isEmptyElement) { while (_reader.NodeType != XmlNodeType.EndElement) { // Skip all the inner child elements if (_reader.NodeType == XmlNodeType.Element) { _reader.Skip(); // We need to skip all the whitespaces following a skipped element. // - If the whitespaces don't contain /r/n, then it's okay to skip them // as part of the element. // - If the whitespaces contain /r/n, not skipping them will result // in a redundant emtpy line being copied. if (_reader.NodeType == XmlNodeType.Whitespace) { _reader.Skip(); } } else { // We want to preserve other content, e.g. comments. CopyXmlNode(utilWriter); } } if (_reader.LineNumber != startingLine) { // The whitespace in front of the EndElement was skipped above. // We need to append spaces to compensate for that. utilWriter.AppendSpacesToLinePosition(TrueLinePosition); } #if DBG Debug.Assert(_reader.Depth == depth, "We should be at the same depth as the opening Element"); #endif // Copy the end element. CopyXmlNode(utilWriter); } return(CopyReaderToNextElement(utilWriter, true)); }
internal StreamWriterCheckpoint(XmlUtilWriter writer) { writer.Flush(); this._lineNumber = writer._lineNumber; this._linePosition = writer._linePosition; this._isLastLineBlank = writer._isLastLineBlank; writer._baseStream.Flush(); this._streamPosition = writer._baseStream.Position; this._streamLength = writer._baseStream.Length; }
private void CopyElement(XmlUtilWriter utilWriter) { int depth = this._reader.Depth; bool isEmptyElement = this._reader.IsEmptyElement; this.CopyXmlNode(utilWriter); while (this._reader.Depth > depth) { this.CopyXmlNode(utilWriter); } if (!isEmptyElement) { this.CopyXmlNode(utilWriter); } }
internal string CopySection() { this.ResetCachedStringWriter(); WhitespaceHandling whitespaceHandling = this._reader.WhitespaceHandling; this._reader.WhitespaceHandling = WhitespaceHandling.All; XmlUtilWriter utilWriter = new XmlUtilWriter(this._cachedStringWriter, false); this.CopyElement(utilWriter); this._reader.WhitespaceHandling = whitespaceHandling; if ((whitespaceHandling == WhitespaceHandling.None) && (this.Reader.NodeType == XmlNodeType.Whitespace)) { this._reader.Read(); } utilWriter.Flush(); return(((StringWriter)utilWriter.Writer).ToString()); }
internal bool SkipAndCopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { int depth; if (!utilWriter.IsLastLineBlank) { this._reader.Skip(); return(this.CopyReaderToNextElement(utilWriter, limitDepth)); } if (limitDepth) { depth = this._reader.Depth; } else { depth = 0; } this._reader.Skip(); int lineNumber = this._reader.LineNumber; while (!this._reader.EOF) { if (this._reader.NodeType != XmlNodeType.Whitespace) { if (this._reader.LineNumber > lineNumber) { utilWriter.SeekToLineStart(); utilWriter.AppendWhiteSpace(lineNumber + 1, 1, this.LineNumber, this.TrueLinePosition); } break; } this._reader.Read(); } while (!this._reader.EOF) { if ((this._reader.NodeType == XmlNodeType.Element) || (this._reader.Depth < depth)) { break; } this.CopyXmlNode(utilWriter); } return(!this._reader.EOF); }
// // Copy the reader until we hit an element, or we've exited the current depth. // internal bool CopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { bool moreToRead = true; // Set the depth if we limit copying to this depth int depth; if (limitDepth) { // there is nothing in the element if (_reader.NodeType == XmlNodeType.EndElement) { return(true); } depth = _reader.Depth; } else { depth = 0; } // Copy nodes until we've reached the desired depth, or until we hit an element. do { if (_reader.NodeType == XmlNodeType.Element) { break; } if (_reader.Depth < depth) { break; } moreToRead = CopyXmlNode(utilWriter); } while (moreToRead); return(moreToRead); }
// // Copy an XML element and its children, up to and including the end element. // private void CopyElement(XmlUtilWriter utilWriter) { Debug.Assert(_reader.NodeType == XmlNodeType.Element, "_reader.NodeType== XmlNodeType.Element"); int depth = _reader.Depth; bool isEmptyElement = _reader.IsEmptyElement; // Copy current node CopyXmlNode(utilWriter); // Copy nodes while the depth is greater than the current depth. while (_reader.Depth > depth) { CopyXmlNode(utilWriter); } // Copy the end element. if (!isEmptyElement) { CopyXmlNode(utilWriter); } }
internal string UpdateStartElement(XmlUtilWriter utilWriter, string updatedStartElement, bool needsChildren, int linePosition, int indent) { string str = null; string str6; bool flag = false; string name = this._reader.Name; if (this._reader.IsEmptyElement) { if ((updatedStartElement == null) && needsChildren) { updatedStartElement = this.RetrieveFullOpenElementTag(); } flag = updatedStartElement != null; } if (updatedStartElement == null) { this.CopyXmlNode(utilWriter); return(str); } string str5 = FormatXmlElement(updatedStartElement + ("</" + name + ">"), linePosition, indent, true); int startIndex = str5.LastIndexOf('\n') + 1; if (flag) { str = str5.Substring(startIndex); str6 = str5.Substring(0, startIndex); } else { str6 = str5.Substring(0, startIndex - 2); } utilWriter.Write(str6); this._reader.Read(); return(str); }
internal StreamWriterCheckpoint(XmlUtilWriter writer) { writer.Flush(); _lineNumber = writer.LineNumber; _linePosition = writer.LinePosition; _isLastLineBlank = writer.IsLastLineBlank; writer._baseStream.Flush(); _streamPosition = writer._baseStream.Position; _streamLength = writer._baseStream.Length; }
private void CopyConfigSource(XmlUtilWriter utilWriter, string updatedXml, string configSourceStreamName, byte[] buffer) { // only copy the byte order mark if it exists in the current web.config byte[] preamble; using (Stream stream = new MemoryStream(buffer)) { using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, true)) { preamble = ConfigStreamInfo.StreamEncoding.GetPreamble(); } } CheckPreamble(preamble, utilWriter, buffer); using (Stream stream = new MemoryStream(buffer)) { using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, false)) { XmlTextReader reader = xmlUtil.Reader; // copy up to the first element reader.WhitespaceHandling = WhitespaceHandling.All; reader.Read(); // determine the indent to use for the element int indent = DEFAULT_INDENT; int linePosition = 1; bool hasElement = xmlUtil.CopyReaderToNextElement(utilWriter, false); if (hasElement) { // find the indent of the first attribute, if any int lineNumber = reader.LineNumber; linePosition = reader.LinePosition - 1; int attributeIndent = 0; while (reader.MoveToNextAttribute()) { if (reader.LineNumber > lineNumber) { attributeIndent = reader.LinePosition - linePosition; break; } } // find the indent of the first sub element, if any int elementIndent = 0; reader.Read(); while (reader.Depth >= 1) { if (reader.NodeType == XmlNodeType.Element) { elementIndent = (reader.LinePosition - 1) - linePosition; break; } reader.Read(); } if (elementIndent > 0) { indent = elementIndent; } else if (attributeIndent > 0) { indent = attributeIndent; } } // Write the config source string formattedXml = XmlUtil.FormatXmlElement(updatedXml, linePosition, indent, true); utilWriter.Write(formattedXml); // Copy remaining contents if (hasElement) { // Skip over the existing element while (reader.Depth > 0) { reader.Read(); } if (reader.IsEmptyElement || reader.NodeType == XmlNodeType.EndElement) { reader.Read(); } // Copy remainder of file while (xmlUtil.CopyXmlNode(utilWriter)) { } } } } }
private void WriteSectionUpdate(XmlUtilWriter utilWriter, DefinitionUpdate update, int linePosition, int indent, bool skipFirstIndent) { ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result; string updatedXml; if (!String.IsNullOrEmpty(configSection.SectionInformation.ConfigSource)) { updatedXml = string.Format(CultureInfo.InvariantCulture, FORMAT_SECTION_CONFIGSOURCE, configSection.SectionInformation.Name, configSection.SectionInformation.ConfigSource); } else { updatedXml = update.UpdatedXml; } string formattedXml = XmlUtil.FormatXmlElement(updatedXml, linePosition, indent, skipFirstIndent); utilWriter.Write(formattedXml); }
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; }
// // Calculate a new indent based on the position of the parent element and the current node. // private int UpdateIndent(int oldIndent, XmlUtil xmlUtil, XmlUtilWriter utilWriter, int parentLinePosition) { int indent = oldIndent; if (xmlUtil.Reader.NodeType == XmlNodeType.Element && utilWriter.IsLastLineBlank) { int childLinePosition = xmlUtil.TrueLinePosition; if (parentLinePosition < childLinePosition && childLinePosition <= parentLinePosition + MAX_INDENT) { indent = childLinePosition - parentLinePosition; } } return indent; }
// Recursively write new sections for each section group. private bool WriteNewConfigDefinitionsRecursive(XmlUtilWriter utilWriter, SectionUpdates sectionUpdates, int linePosition, int indent, bool skipFirstIndent) { bool wroteASection = false; string[] movedSectionNames = sectionUpdates.GetMovedSectionNames(); if (movedSectionNames != null) { wroteASection = true; foreach (string configKey in movedSectionNames) { DefinitionUpdate update = sectionUpdates.GetDefinitionUpdate(configKey); WriteSectionUpdate(utilWriter, update, linePosition, indent, skipFirstIndent); utilWriter.AppendNewLine(); skipFirstIndent = false; } } string[] newGroupNames = sectionUpdates.GetNewGroupNames(); if (newGroupNames != null) { foreach (string group in newGroupNames) { if (TargetFramework != null) { ConfigurationSectionGroup g = GetSectionGroup(group); if (g != null && !g.ShouldSerializeSectionGroupInTargetVersion(TargetFramework)){ sectionUpdates.MarkGroupAsRetrieved(group); continue; } } if (!skipFirstIndent) { utilWriter.AppendSpacesToLinePosition(linePosition); } skipFirstIndent = false; utilWriter.Write("<" + group + ">\r\n"); bool recurseWroteASection = WriteNewConfigDefinitionsRecursive( utilWriter, sectionUpdates.GetSectionUpdatesForGroup(group), linePosition + indent, indent, false); if (recurseWroteASection) { wroteASection = true; } utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write("</" + group + ">\r\n"); } } sectionUpdates.IsNew = false; return wroteASection; }
private void WriteUnwrittenConfigDeclarationsRecursive(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) { string[] unretrievedSectionNames = declarationUpdates.GetUnretrievedSectionNames(); if (unretrievedSectionNames != null) { foreach (string configKey in unretrievedSectionNames) { Debug.Assert(!IsImplicitSection(configKey), "We should never write out an implicit section"); if (!skipFirstIndent) { utilWriter.AppendSpacesToLinePosition(linePosition); } skipFirstIndent = false; DeclarationUpdate update = declarationUpdates.GetDeclarationUpdate(configKey); if (update != null && !string.IsNullOrEmpty(update.UpdatedXml)) { utilWriter.Write(update.UpdatedXml); utilWriter.AppendNewLine(); } } } string[] unretrievedGroupNames = declarationUpdates.GetUnretrievedGroupNames(); if (unretrievedGroupNames != null) { foreach (string group in unretrievedGroupNames) { if (TargetFramework != null) { ConfigurationSectionGroup g = GetSectionGroup(group); if (g != null && !g.ShouldSerializeSectionGroupInTargetVersion(TargetFramework)){ declarationUpdates.MarkGroupAsRetrieved(group); continue; } } if (!skipFirstIndent) { utilWriter.AppendSpacesToLinePosition(linePosition); } skipFirstIndent = false; SectionUpdates declarationUpdatesChild = declarationUpdates.GetSectionUpdatesForGroup(group); DeclarationUpdate groupUpdate = declarationUpdatesChild.GetSectionGroupUpdate(); if (groupUpdate == null) { utilWriter.Write("<sectionGroup name=\"" + group + "\">"); } else { utilWriter.Write(groupUpdate.UpdatedXml); } utilWriter.AppendNewLine(); WriteUnwrittenConfigDeclarationsRecursive(declarationUpdatesChild, utilWriter, linePosition + indent, indent, false); utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write("</sectionGroup>\r\n"); } } }
private void WriteNewConfigDeclarations(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) { if (!skipFirstIndent) { utilWriter.AppendSpacesToLinePosition(linePosition); } utilWriter.Write("<configSections>\r\n"); WriteUnwrittenConfigDeclarations(declarationUpdates, utilWriter, linePosition + indent, indent, false); utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write("</configSections>\r\n"); if (skipFirstIndent) { utilWriter.AppendSpacesToLinePosition(linePosition); } }
// // Update the config file with the changes in each ConfigurationSection // internal void SaveAs(string filename, ConfigurationSaveMode saveMode, bool forceUpdateAll) { // Get the updates. SectionUpdates declarationUpdates = GetConfigDeclarationUpdates(saveMode, forceUpdateAll); ConfigDefinitionUpdates definitionUpdates; ArrayList configSourceUpdates; bool checkedConfigForUpdates = false; bool requireUpdates = (filename != null); GetConfigDefinitionUpdates(requireUpdates, saveMode, forceUpdateAll, out definitionUpdates, out configSourceUpdates); if (filename != null) { Debug.Assert(filename.Length > 0, "The caller should make sure that filename is not empty"); // // Verify that the filename is not being used. // // Note that if we are using a remote host, all the streamName's in _streamInfoUpdates // are actually fullpaths on the remote machine. In this case there is no way to // detect if we have a conflict or not. if (!Host.IsRemote && _streamInfoUpdates.Contains(filename)) { throw new ArgumentException(SR.GetString(SR.Filename_in_SaveAs_is_used_already, filename)); } // // If there was no config file for this config record, // record the new stream name and version. // if (String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) { StreamInfo streamInfo = new StreamInfo(null, null, filename); _streamInfoUpdates.Add(filename, streamInfo); ConfigStreamInfo.StreamName = filename; ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName); } // // Update the host to redirect filenames // UpdateConfigHost.AddStreamname(ConfigStreamInfo.StreamName, filename, Host.IsRemote); // Redirect also all configSource filenames foreach (StreamInfo streamInfo in _streamInfoUpdates.Values) { if (!String.IsNullOrEmpty(streamInfo.SectionName)) { // Get the new configSource streamName based on the new filename path string newStreamName = InternalConfigHost.StaticGetStreamNameForConfigSource( filename, streamInfo.ConfigSource); // Ask UpdateConfigHost to intercept them. UpdateConfigHost.AddStreamname(streamInfo.StreamName, newStreamName, Host.IsRemote); } } } if (!requireUpdates) { // Check if there are any updates needed for the // configuration record itself. requireUpdates = RecordItselfRequiresUpdates; } if (declarationUpdates != null || definitionUpdates != null || requireUpdates) { // Copy the input stream before opening the output stream. byte[] readBuffer = null; Encoding encoding = null; if (ConfigStreamInfo.HasStream) { using (Stream streamRead = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) { if (streamRead == null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), ConfigStreamInfo.StreamName, 0); } readBuffer = new byte[streamRead.Length]; int count = streamRead.Read(readBuffer, 0, (int) streamRead.Length); if (count != streamRead.Length) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_data_read_count_mismatch)); } } // Read the first byte so that we can determine the encoding. try { using (StreamReader reader = new StreamReader(ConfigStreamInfo.StreamName)) { if (reader.Peek() >= 0) { reader.Read(); } // Dev10 bug 687017 - Handle only UTF-16 explicitly, so that handling of other // encodings are not affected. if (reader.CurrentEncoding is UnicodeEncoding) { encoding = reader.CurrentEncoding; } } } catch { // Ignore any errors, encoding will remain null. } } string changedStreamName = FindChangedConfigurationStream(); if (changedStreamName != null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), changedStreamName, 0); } checkedConfigForUpdates = true; // Write the changes to the output stream. object writeContext = null; bool streamOpened = false; try { try { using (Stream streamWrite = Host.OpenStreamForWrite(ConfigStreamInfo.StreamName, null, ref writeContext)) { streamOpened = true; // Use the default StreamWriter constructor if encoding is null, // otherwise specify the encoding. using (StreamWriter streamWriter = encoding == null ? new StreamWriter(streamWrite) : new StreamWriter(streamWrite, encoding)) { XmlUtilWriter utilWriter = new XmlUtilWriter(streamWriter, true); if (ConfigStreamInfo.HasStream) { CopyConfig(declarationUpdates, definitionUpdates, readBuffer, ConfigStreamInfo.StreamName, NamespaceChangeNeeded, utilWriter); } else { CreateNewConfig(declarationUpdates, definitionUpdates, NamespaceChangeNeeded, utilWriter); } } } } catch { if (streamOpened) { Host.WriteCompleted(ConfigStreamInfo.StreamName, false, writeContext); } throw; } } // // Guarantee that exceptions contain at least the name of the stream by wrapping them // in a ConfigurationException. // catch (Exception e) { throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0); } Host.WriteCompleted(ConfigStreamInfo.StreamName, true, writeContext); // Update stream information for the config file ConfigStreamInfo.HasStream = true; ConfigStreamInfo.ClearStreamInfos(); ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName); } if (configSourceUpdates != null) { // If we haven't checked before, check now if (!checkedConfigForUpdates) { string changedStreamName = FindChangedConfigurationStream(); if (changedStreamName != null) { throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), changedStreamName, 0); } } // write updates foreach (DefinitionUpdate update in configSourceUpdates) { SaveConfigSource(update); } } // Update state to reflect the changes to the config file UpdateRecords(); }
internal bool CopyXmlNode(XmlUtilWriter utilWriter) { string s = null; int fromLineNumber = -1; int fromLinePosition = -1; int lineNumber = 0; int linePosition = 0; int num5 = 0; int num6 = 0; if (utilWriter.TrackPosition) { lineNumber = this._reader.LineNumber; linePosition = this._reader.LinePosition; num5 = utilWriter.LineNumber; num6 = utilWriter.LinePosition; } XmlNodeType nodeType = this._reader.NodeType; switch (nodeType) { case XmlNodeType.Whitespace: utilWriter.Write(this._reader.Value); break; case XmlNodeType.Element: s = this._reader.IsEmptyElement ? "/>" : ">"; fromLineNumber = this._reader.LineNumber; fromLinePosition = this._reader.LinePosition + this._reader.Name.Length; utilWriter.Write('<'); utilWriter.Write(this._reader.Name); while (this._reader.MoveToNextAttribute()) { int toLineNumber = this._reader.LineNumber; int toLinePosition = this._reader.LinePosition; utilWriter.AppendRequiredWhiteSpace(fromLineNumber, fromLinePosition, toLineNumber, toLinePosition); int num9 = utilWriter.Write(this._reader.Name) + utilWriter.Write('='); num9 += utilWriter.AppendAttributeValue(this._reader); fromLineNumber = toLineNumber; fromLinePosition = toLinePosition + num9; } break; case XmlNodeType.EndElement: s = ">"; fromLineNumber = this._reader.LineNumber; fromLinePosition = this._reader.LinePosition + this._reader.Name.Length; utilWriter.Write("</"); utilWriter.Write(this._reader.Name); break; case XmlNodeType.Comment: utilWriter.AppendComment(this._reader.Value); break; case XmlNodeType.Text: utilWriter.AppendEscapeTextString(this._reader.Value); break; case XmlNodeType.XmlDeclaration: s = "?>"; fromLineNumber = this._reader.LineNumber; fromLinePosition = this._reader.LinePosition + 3; utilWriter.Write("<?xml"); while (this._reader.MoveToNextAttribute()) { int num10 = this._reader.LineNumber; int num11 = this._reader.LinePosition; utilWriter.AppendRequiredWhiteSpace(fromLineNumber, fromLinePosition, num10, num11); int num12 = utilWriter.Write(this._reader.Name) + utilWriter.Write('='); num12 += utilWriter.AppendAttributeValue(this._reader); fromLineNumber = num10; fromLinePosition = num11 + num12; } this._reader.MoveToElement(); break; case XmlNodeType.SignificantWhitespace: utilWriter.Write(this._reader.Value); break; case XmlNodeType.ProcessingInstruction: utilWriter.AppendProcessingInstruction(this._reader.Name, this._reader.Value); break; case XmlNodeType.EntityReference: utilWriter.AppendEntityRef(this._reader.Name); break; case XmlNodeType.CDATA: utilWriter.AppendCData(this._reader.Value); break; default: if (nodeType == XmlNodeType.DocumentType) { int num13 = utilWriter.Write("<!DOCTYPE"); utilWriter.AppendRequiredWhiteSpace(this._lastLineNumber, this._lastLinePosition + num13, this._reader.LineNumber, this._reader.LinePosition); utilWriter.Write(this._reader.Name); string str2 = null; if (this._reader.HasValue) { str2 = this._reader.Value; } fromLineNumber = this._reader.LineNumber; fromLinePosition = this._reader.LinePosition + this._reader.Name.Length; if (this._reader.MoveToFirstAttribute()) { utilWriter.AppendRequiredWhiteSpace(fromLineNumber, fromLinePosition, this._reader.LineNumber, this._reader.LinePosition); string name = this._reader.Name; utilWriter.Write(name); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(this._reader); this._reader.MoveToAttribute(0); if (name == "PUBLIC") { this._reader.MoveToAttribute(1); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(this._reader); this._reader.MoveToAttribute(1); } } if ((str2 != null) && (str2.Length > 0)) { utilWriter.Write(" ["); utilWriter.Write(str2); utilWriter.Write(']'); } utilWriter.Write('>'); } break; } bool flag = this._reader.Read(); nodeType = this._reader.NodeType; if (s != null) { int positionOffset = GetPositionOffset(nodeType); int num15 = this._reader.LineNumber; int num16 = (this._reader.LinePosition - positionOffset) - s.Length; utilWriter.AppendWhiteSpace(fromLineNumber, fromLinePosition, num15, num16); utilWriter.Write(s); } if (utilWriter.TrackPosition) { this._lastLineNumber = (lineNumber - num5) + utilWriter.LineNumber; if (num5 == utilWriter.LineNumber) { this._lastLinePosition = (linePosition - num6) + utilWriter.LinePosition; return(flag); } this._lastLinePosition = utilWriter.LinePosition; } return(flag); }
// Format an Xml element to be written to the config file. // Params: // xmlElement - the element // linePosition - start position of the element // indent - indent for each depth // skipFirstIndent - skip indent for the first element? // static internal string FormatXmlElement(string xmlElement, int linePosition, int indent, bool skipFirstIndent) { XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.Unicode); XmlTextReader reader = new XmlTextReader(xmlElement, XmlNodeType.Element, context); StringWriter stringWriter = new StringWriter(new StringBuilder(64), CultureInfo.InvariantCulture); XmlUtilWriter utilWriter = new XmlUtilWriter(stringWriter, false); // append newline before indent? bool newLine = false; // last node visited was text? bool lastWasText = false; // width of line from end of indentation int lineWidth; // length of the stringbuilder after last indent with newline int sbLengthLastNewLine = 0; while (reader.Read()) { XmlNodeType nodeType = reader.NodeType; if (lastWasText) { utilWriter.Flush(); lineWidth = sbLengthLastNewLine - ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } else { lineWidth = 0; } switch (nodeType) { case XmlNodeType.CDATA: case XmlNodeType.Element: case XmlNodeType.EndElement: case XmlNodeType.Comment: // Do not indent if the last node was text - doing so would add whitespace // that is included as part of the text. if (!skipFirstIndent && !lastWasText) { utilWriter.AppendIndent(linePosition, indent, reader.Depth, newLine); if (newLine) { utilWriter.Flush(); sbLengthLastNewLine = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } } break; default: break; } lastWasText = false; switch (nodeType) { case XmlNodeType.Whitespace: break; case XmlNodeType.SignificantWhitespace: utilWriter.Write(reader.Value); break; case XmlNodeType.CDATA: utilWriter.AppendCData(reader.Value); break; case XmlNodeType.ProcessingInstruction: utilWriter.AppendProcessingInstruction(reader.Name, reader.Value); break; case XmlNodeType.Comment: utilWriter.AppendComment(reader.Value); break; case XmlNodeType.Text: utilWriter.AppendEscapeTextString(reader.Value); lastWasText = true; break; case XmlNodeType.Element: { // Write "<elem" utilWriter.Write('<'); utilWriter.Write(reader.Name); lineWidth += reader.Name.Length + 2; int c = reader.AttributeCount; for (int i = 0; i < c; i++) { // Add new line if we've exceeded the line width bool writeSpace; if (lineWidth > MAX_LINE_WIDTH) { utilWriter.AppendIndent(linePosition, indent, reader.Depth - 1, true); lineWidth = indent; writeSpace = false; utilWriter.Flush(); sbLengthLastNewLine = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } else { writeSpace = true; } // Write the attribute reader.MoveToNextAttribute(); utilWriter.Flush(); int startLength = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; if (writeSpace) { utilWriter.AppendSpace(); } utilWriter.Write(reader.Name); utilWriter.Write('='); utilWriter.AppendAttributeValue(reader); utilWriter.Flush(); lineWidth += ((StringWriter)utilWriter.Writer).GetStringBuilder().Length - startLength; } } // position reader back on element reader.MoveToElement(); // write closing tag if (reader.IsEmptyElement) { utilWriter.Write(" />"); } else { utilWriter.Write('>'); } break; case XmlNodeType.EndElement: utilWriter.Write("</"); utilWriter.Write(reader.Name); utilWriter.Write('>'); break; case XmlNodeType.EntityReference: utilWriter.AppendEntityRef(reader.Name); break; // Ignore <?xml and <!DOCTYPE nodes case XmlNodeType.XmlDeclaration: case XmlNodeType.DocumentType: default: break; } // put each new element on a new line newLine = true; // do not skip any more indents skipFirstIndent = false; } utilWriter.Flush(); string s = ((StringWriter)utilWriter.Writer).ToString(); return(s); }
internal static string FormatXmlElement(string xmlElement, int linePosition, int indent, bool skipFirstIndent) { XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.Unicode); XmlTextReader reader = new XmlTextReader(xmlElement, XmlNodeType.Element, context); StringWriter writer = new StringWriter(new StringBuilder(0x40), CultureInfo.InvariantCulture); XmlUtilWriter writer2 = new XmlUtilWriter(writer, false); bool newLine = false; bool flag2 = false; int num2 = 0; while (reader.Read()) { int num; int attributeCount; int num4; bool flag3; XmlNodeType nodeType = reader.NodeType; if (flag2) { writer2.Flush(); num = num2 - ((StringWriter)writer2.Writer).GetStringBuilder().Length; } else { num = 0; } XmlNodeType type2 = nodeType; if (type2 <= XmlNodeType.CDATA) { switch (type2) { case XmlNodeType.Element: case XmlNodeType.CDATA: goto Label_0091; } goto Label_00CA; } if ((type2 != XmlNodeType.Comment) && (type2 != XmlNodeType.EndElement)) { goto Label_00CA; } Label_0091: if (!skipFirstIndent && !flag2) { writer2.AppendIndent(linePosition, indent, reader.Depth, newLine); if (newLine) { writer2.Flush(); num2 = ((StringWriter)writer2.Writer).GetStringBuilder().Length; } } Label_00CA: flag2 = false; switch (nodeType) { case XmlNodeType.Element: writer2.Write('<'); writer2.Write(reader.Name); num += reader.Name.Length + 2; attributeCount = reader.AttributeCount; num4 = 0; goto Label_0274; case XmlNodeType.Text: writer2.AppendEscapeTextString(reader.Value); flag2 = true; goto Label_02D6; case XmlNodeType.CDATA: writer2.AppendCData(reader.Value); goto Label_02D6; case XmlNodeType.EntityReference: writer2.AppendEntityRef(reader.Name); goto Label_02D6; case XmlNodeType.ProcessingInstruction: writer2.AppendProcessingInstruction(reader.Name, reader.Value); goto Label_02D6; case XmlNodeType.Comment: writer2.AppendComment(reader.Value); goto Label_02D6; case XmlNodeType.SignificantWhitespace: writer2.Write(reader.Value); goto Label_02D6; case XmlNodeType.EndElement: writer2.Write("</"); writer2.Write(reader.Name); writer2.Write('>'); goto Label_02D6; default: goto Label_02D6; } Label_01BE: if (num > 60) { writer2.AppendIndent(linePosition, indent, reader.Depth - 1, true); num = indent; flag3 = false; writer2.Flush(); num2 = ((StringWriter)writer2.Writer).GetStringBuilder().Length; } else { flag3 = true; } reader.MoveToNextAttribute(); writer2.Flush(); int length = ((StringWriter)writer2.Writer).GetStringBuilder().Length; if (flag3) { writer2.AppendSpace(); } writer2.Write(reader.Name); writer2.Write('='); writer2.AppendAttributeValue(reader); writer2.Flush(); num += ((StringWriter)writer2.Writer).GetStringBuilder().Length - length; num4++; Label_0274: if (num4 < attributeCount) { goto Label_01BE; } reader.MoveToElement(); if (reader.IsEmptyElement) { writer2.Write(" />"); } else { writer2.Write('>'); } Label_02D6: newLine = true; skipFirstIndent = false; } writer2.Flush(); return(((StringWriter)writer2.Writer).ToString()); }
private void CreateNewConfigSource(XmlUtilWriter utilWriter, string updatedXml, int indent) { string formattedXml = XmlUtil.FormatXmlElement(updatedXml, 0, indent, true); utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FormatConfigsourceFile, ConfigStreamInfo.StreamEncoding.WebName)); utilWriter.Write(formattedXml + NewLine); }
// Create a new config file. private void CreateNewConfig( SectionUpdates declarationUpdates, ConfigDefinitionUpdates definitionUpdates, NamespaceChange namespaceChange, XmlUtilWriter utilWriter) { // Write Header utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FormatNewconfigfile, ConfigStreamInfo.StreamEncoding.WebName)); // Write <configuration> tag if (namespaceChange == NamespaceChange.Add) { utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FormatConfigurationNamespace, KeywordConfigurationNamespace)); } else utilWriter.Write(FormatConfiguration); const int LineIndent = DefaultIndent + 1; if (declarationUpdates != null) WriteNewConfigDeclarations(declarationUpdates, utilWriter, LineIndent, DefaultIndent, false); WriteNewConfigDefinitions(definitionUpdates, utilWriter, LineIndent, DefaultIndent); utilWriter.Write(FormatConfigurationEndelement); }
// // Copy a configuration section to a string, and advance the reader. // internal string CopySection() { ResetCachedStringWriter(); // Preserve whitespace for sections for backcompat WhitespaceHandling originalHandling = _reader.WhitespaceHandling; _reader.WhitespaceHandling = WhitespaceHandling.All; // Create string writer to write to XmlUtilWriter utilWriter = new XmlUtilWriter(_cachedStringWriter, false); // Copy the element CopyElement(utilWriter); // Reset whitespace handling _reader.WhitespaceHandling = originalHandling; if ((originalHandling == WhitespaceHandling.None) && (Reader.NodeType == XmlNodeType.Whitespace)) { // If we were previously suppose to skip whitespace, and now we // are at it, then lets jump to the next item _reader.Read(); } utilWriter.Flush(); string s = ((StringWriter)utilWriter.Writer).ToString(); return s; }
// // Copy a single XML node, attempting to preserve whitespace. // A side effect of this method is to advance the reader to the next node. // // PERFORMANCE NOTE: this function is used at runtime to copy a configuration section, // and at designtime to copy an entire XML document. // // At designtime, this function needs to be able to copy a <!DOCTYPE declaration. // Copying a <!DOCTYPE declaration is expensive, because due to limitations of the // XmlReader API, we must track the position of the writer to accurately format it. // Tracking the position of the writer is expensive, as it requires examining every // character that is written for newline characters, and maintaining the seek position // of the underlying stream at each new line, which in turn requires a stream flush. // // This function must NEVER require tracking the writer position to copy the Xml nodes // that are used in a configuration section. // internal bool CopyXmlNode(XmlUtilWriter utilWriter) { // // For nodes that have a closing string, such as "<element >" // the XmlReader API does not give us the location of the closing string, e.g. ">". // To correctly determine the location of the closing part, we advance the reader, // determine the position of the next node, then work backwards to add whitespace // and add the closing string. // string close = null; int lineNumber = -1; int linePosition = -1; int readerLineNumber = 0; int readerLinePosition = 0; int writerLineNumber = 0; int writerLinePosition = 0; if (utilWriter.TrackPosition) { readerLineNumber = _reader.LineNumber; readerLinePosition = _reader.LinePosition; writerLineNumber = utilWriter.LineNumber; writerLinePosition = utilWriter.LinePosition; } // We test the node type in the likely order of decreasing occurrence. XmlNodeType nodeType = _reader.NodeType; if (nodeType == XmlNodeType.Whitespace) { utilWriter.Write(_reader.Value); } else if (nodeType == XmlNodeType.Element) { close = (_reader.IsEmptyElement) ? "/>" : ">"; // get the line position after the element declaration: // <element attr="value" // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; utilWriter.Write('<'); utilWriter.Write(_reader.Name); // // Note that there is no way to get spacing between attribute name and value // For example: // // <elem attr="value" /> // // is reported with the same position as // // <elem attr = "value" /> // // The first example has no spaces around '=', the second example does. // while (_reader.MoveToNextAttribute()) { // get line position of the attribute declaration // <element attr="value" // ^ // attrLinePosition // int attrLineNumber = _reader.LineNumber; int attrLinePosition = _reader.LinePosition; // Write the whitespace before the attribute utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, attrLineNumber, attrLinePosition); // Write the attribute and value int charactersWritten = utilWriter.Write(_reader.Name); charactersWritten += utilWriter.Write('='); charactersWritten += utilWriter.AppendAttributeValue(_reader); // Update position. Note that the attribute value is escaped to always be on a single line. lineNumber = attrLineNumber; linePosition = attrLinePosition + charactersWritten; } } else if (nodeType == XmlNodeType.EndElement) { close = ">"; // get line position after the end element declaration: // </element > // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; utilWriter.Write("</"); utilWriter.Write(_reader.Name); } else if (nodeType == XmlNodeType.Comment) { utilWriter.AppendComment(_reader.Value); } else if (nodeType == XmlNodeType.Text) { utilWriter.AppendEscapeTextString(_reader.Value); } else if (nodeType == XmlNodeType.XmlDeclaration) { close = "?>"; // get line position after the xml declaration: // <?xml version="1.0" // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + 3; utilWriter.Write("<?xml"); // // Note that there is no way to get spacing between attribute name and value // For example: // // <?xml attr="value" ?> // // is reported with the same position as // // <?xml attr = "value" ?> // // The first example has no spaces around '=', the second example does. // while (_reader.MoveToNextAttribute()) { // get line position of the attribute declaration // <?xml version="1.0" // ^ // attrLinePosition // int attrLineNumber = _reader.LineNumber; int attrLinePosition = _reader.LinePosition; // Write the whitespace before the attribute utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, attrLineNumber, attrLinePosition); // Write the attribute and value int charactersWritten = utilWriter.Write(_reader.Name); charactersWritten += utilWriter.Write('='); charactersWritten += utilWriter.AppendAttributeValue(_reader); // Update position. Note that the attribute value is escaped to always be on a single line. lineNumber = attrLineNumber; linePosition = attrLinePosition + charactersWritten; } // Position reader at beginning of node _reader.MoveToElement(); } else if (nodeType == XmlNodeType.SignificantWhitespace) { utilWriter.Write(_reader.Value); } else if (nodeType == XmlNodeType.ProcessingInstruction) { // // Note that there is no way to get spacing between attribute name and value // For example: // // <?pi "value" ?> // // is reported with the same position as // // <?pi "value" ?> // // The first example has one space between 'pi' and "value", the second has multiple spaces. // utilWriter.AppendProcessingInstruction(_reader.Name, _reader.Value); } else if (nodeType == XmlNodeType.EntityReference) { utilWriter.AppendEntityRef(_reader.Name); } else if (nodeType == XmlNodeType.CDATA) { utilWriter.AppendCData(_reader.Value); } else if (nodeType == XmlNodeType.DocumentType) { // // XmlNodeType.DocumentType has the following format: // // <!DOCTYPE rootElementName {(SYSTEM uriRef)|(PUBLIC id uriRef)} {[ dtdDecls ]} > // // The reader only gives us the position of 'rootElementName', so we must track what was // written before "<!DOCTYPE" in order to correctly determine the position of the // <!DOCTYPE tag // Debug.Assert(utilWriter.TrackPosition, "utilWriter.TrackPosition"); int c = utilWriter.Write("<!DOCTYPE"); // Write the space between <!DOCTYPE and the rootElementName utilWriter.AppendRequiredWhiteSpace(_lastLineNumber, _lastLinePosition + c, _reader.LineNumber, _reader.LinePosition); // Write the rootElementName utilWriter.Write(_reader.Name); // Get the dtd declarations, if any string dtdValue = null; if (_reader.HasValue) { dtdValue = _reader.Value; } // get line position after the !DOCTYPE declaration: // <!DOCTYPE rootElement SYSTEM rootElementDtdUri > // ^ // linePosition lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; // Note that there is no way to get the spacing after PUBLIC or SYSTEM attributes and their values if (_reader.MoveToFirstAttribute()) { // Write the space before SYSTEM or PUBLIC utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, _reader.LineNumber, _reader.LinePosition); // Write SYSTEM or PUBLIC and the 1st value of the attribute string attrName = _reader.Name; utilWriter.Write(attrName); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(_reader); _reader.MoveToAttribute(0); // If PUBLIC, write the second value of the attribute if (attrName == "PUBLIC") { _reader.MoveToAttribute(1); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(_reader); _reader.MoveToAttribute(1); } } // If there is a dtd, write it if (dtdValue != null && dtdValue.Length > 0) { utilWriter.Write(" ["); utilWriter.Write(dtdValue); utilWriter.Write(']'); } utilWriter.Write('>'); } // Advance the _reader so we can get the position of the next node. bool moreToRead = _reader.Read(); nodeType = _reader.NodeType; // Close the node we are copying. if (close != null) { // // Find the position of the close string, for example: // // <element > <subElement /> // ^ // closeLinePosition // int startOffset = GetPositionOffset(nodeType); int closeLineNumber = _reader.LineNumber; int closeLinePosition = _reader.LinePosition - startOffset - close.Length; // Add whitespace up to the position of the close string utilWriter.AppendWhiteSpace(lineNumber, linePosition, closeLineNumber, closeLinePosition); // Write the close string utilWriter.Write(close); } // // Track the position of the reader based on the position of the reader // before we copied this node and what we have written in copying the node. // This allows us to determine the position of the <!DOCTYPE tag. // if (utilWriter.TrackPosition) { _lastLineNumber = (readerLineNumber - writerLineNumber) + utilWriter.LineNumber; if (writerLineNumber == utilWriter.LineNumber) { _lastLinePosition = (readerLinePosition - writerLinePosition) + utilWriter.LinePosition; } else { _lastLinePosition = utilWriter.LinePosition; } } return(moreToRead); }
// Create a new config file. private void CreateNewConfig( SectionUpdates declarationUpdates, ConfigDefinitionUpdates definitionUpdates, NamespaceChange namespaceChange, XmlUtilWriter utilWriter) { int linePosition = DEFAULT_INDENT + 1; int indent = DEFAULT_INDENT; // Write Header utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FORMAT_NEWCONFIGFILE, ConfigStreamInfo.StreamEncoding.WebName)); // Write <configuration> tag if (namespaceChange == NamespaceChange.Add) { utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FORMAT_CONFIGURATION_NAMESPACE, KEYWORD_CONFIGURATION_NAMESPACE)); } else { utilWriter.Write(FORMAT_CONFIGURATION); } if (declarationUpdates != null) { WriteNewConfigDeclarations(declarationUpdates, utilWriter, linePosition, indent, false); } WriteNewConfigDefinitions(definitionUpdates, utilWriter, linePosition, indent); utilWriter.Write(FORMAT_CONFIGURATION_ENDELEMENT); }
// Format an Xml element to be written to the config file. // Params: // xmlElement - the element // linePosition - start position of the element // indent - indent for each depth // skipFirstIndent - skip indent for the first element? // static internal string FormatXmlElement(string xmlElement, int linePosition, int indent, bool skipFirstIndent) { XmlParserContext context = new XmlParserContext(null, null, null, XmlSpace.Default, Encoding.Unicode); XmlTextReader reader = new XmlTextReader(xmlElement, XmlNodeType.Element, context); StringWriter stringWriter = new StringWriter(new StringBuilder(64), CultureInfo.InvariantCulture); XmlUtilWriter utilWriter = new XmlUtilWriter(stringWriter, false); // append newline before indent? bool newLine = false; // last node visited was text? bool lastWasText = false; // width of line from end of indentation int lineWidth; // length of the stringbuilder after last indent with newline int sbLengthLastNewLine = 0; while (reader.Read()) { XmlNodeType nodeType = reader.NodeType; if (lastWasText) { utilWriter.Flush(); lineWidth = sbLengthLastNewLine - ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } else { lineWidth = 0; } switch (nodeType) { case XmlNodeType.CDATA: case XmlNodeType.Element: case XmlNodeType.EndElement: case XmlNodeType.Comment: // Do not indent if the last node was text - doing so would add whitespace // that is included as part of the text. if (!skipFirstIndent && !lastWasText) { utilWriter.AppendIndent(linePosition, indent, reader.Depth, newLine); if (newLine) { utilWriter.Flush(); sbLengthLastNewLine = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } } break; default: break; } lastWasText = false; switch (nodeType) { case XmlNodeType.Whitespace: break; case XmlNodeType.SignificantWhitespace: utilWriter.Write(reader.Value); break; case XmlNodeType.CDATA: utilWriter.AppendCData(reader.Value); break; case XmlNodeType.ProcessingInstruction: utilWriter.AppendProcessingInstruction(reader.Name, reader.Value); break; case XmlNodeType.Comment: utilWriter.AppendComment(reader.Value); break; case XmlNodeType.Text: utilWriter.AppendEscapeTextString(reader.Value); lastWasText = true; break; case XmlNodeType.Element: { // Write "<elem" utilWriter.Write('<'); utilWriter.Write(reader.Name); lineWidth += reader.Name.Length + 2; int c = reader.AttributeCount; for (int i = 0; i < c; i++) { // Add new line if we've exceeded the line width bool writeSpace; if (lineWidth > MAX_LINE_WIDTH) { utilWriter.AppendIndent(linePosition, indent, reader.Depth - 1, true); lineWidth = indent; writeSpace = false; utilWriter.Flush(); sbLengthLastNewLine = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; } else { writeSpace = true; } // Write the attribute reader.MoveToNextAttribute(); utilWriter.Flush(); int startLength = ((StringWriter)utilWriter.Writer).GetStringBuilder().Length; if (writeSpace) { utilWriter.AppendSpace(); } utilWriter.Write(reader.Name); utilWriter.Write('='); utilWriter.AppendAttributeValue(reader); utilWriter.Flush(); lineWidth += ((StringWriter)utilWriter.Writer).GetStringBuilder().Length - startLength; } } // position reader back on element reader.MoveToElement(); // write closing tag if (reader.IsEmptyElement) { utilWriter.Write(" />"); } else { utilWriter.Write('>'); } break; case XmlNodeType.EndElement: utilWriter.Write("</"); utilWriter.Write(reader.Name); utilWriter.Write('>'); break; case XmlNodeType.EntityReference: utilWriter.AppendEntityRef(reader.Name); break; // Ignore <?xml and <!DOCTYPE nodes case XmlNodeType.XmlDeclaration: case XmlNodeType.DocumentType: default: break; } // put each new element on a new line newLine = true; // do not skip any more indents skipFirstIndent = false; } utilWriter.Flush(); string s = ((StringWriter)utilWriter.Writer).ToString(); return s; }
private void WriteUnwrittenConfigDeclarations(SectionUpdates declarationUpdates, XmlUtilWriter utilWriter, int linePosition, int indent, bool skipFirstIndent) { WriteUnwrittenConfigDeclarationsRecursive(declarationUpdates, utilWriter, linePosition, indent, skipFirstIndent); }
// // Copy an XML element, then continue copying until we've hit the next element // or exited this depth. // internal bool CopyOuterXmlToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { CopyElement(utilWriter); // Copy until reaching the next element, or if limitDepth == true until we've exited this depth. return CopyReaderToNextElement(utilWriter, limitDepth); }
private void WriteNewConfigDefinitions(ConfigDefinitionUpdates configDefinitionUpdates, XmlUtilWriter utilWriter, int linePosition, int indent) { if (configDefinitionUpdates == null) return; foreach (LocationUpdates locationUpdates in configDefinitionUpdates.LocationUpdatesList) { SectionUpdates sectionUpdates = locationUpdates.SectionUpdates; if (sectionUpdates.IsEmpty || !sectionUpdates.IsNew) continue; configDefinitionUpdates.FlagLocationWritten(); bool writeLocationTag = _locationSubPath != null || !locationUpdates.IsDefault; int recurseLinePosition = linePosition; utilWriter.AppendSpacesToLinePosition(linePosition); if (writeLocationTag) { // write the <location> start tag if (_locationSubPath == null) { utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_NOPATH, locationUpdates.OverrideMode.LocationTagXmlString, BoolToString(locationUpdates.InheritInChildApps))); } else { utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_PATH, locationUpdates.OverrideMode.LocationTagXmlString, BoolToString(locationUpdates.InheritInChildApps), _locationSubPath)); } recurseLinePosition += indent; utilWriter.AppendSpacesToLinePosition(recurseLinePosition); } // Invoke the recursive write. WriteNewConfigDefinitionsRecursive(utilWriter, locationUpdates.SectionUpdates, recurseLinePosition, indent, true); if (writeLocationTag) { // Write the location end tag utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write(FORMAT_LOCATION_ENDELEMENT); utilWriter.AppendNewLine(); } } if (configDefinitionUpdates.RequireLocation) { Debug.Assert(IsLocationConfig, "IsLocationConfig"); // If we still require this to be written, then we must write it out now configDefinitionUpdates.FlagLocationWritten(); utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write(String.Format(CultureInfo.InvariantCulture, FORMAT_LOCATION_PATH, OverrideModeSetting.LocationDefault.LocationTagXmlString, KEYWORD_TRUE, _locationSubPath)); utilWriter.AppendSpacesToLinePosition(linePosition); utilWriter.Write(FORMAT_LOCATION_ENDELEMENT); utilWriter.AppendNewLine(); } }
internal bool CopyOuterXmlToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { this.CopyElement(utilWriter); return(this.CopyReaderToNextElement(utilWriter, limitDepth)); }
private void CheckPreamble(byte[] preamble, XmlUtilWriter utilWriter, byte[] buffer) { bool hasByteOrderMark = false; using (Stream preambleStream = new MemoryStream(buffer)) { byte[] streamStart = new byte[preamble.Length]; if (preambleStream.Read(streamStart, 0, streamStart.Length) == streamStart.Length) { hasByteOrderMark = true; for (int i = 0; i < streamStart.Length; i++) { if (streamStart[i] != preamble[i]) { hasByteOrderMark = false; break; } } } } if (!hasByteOrderMark) { // Force the writer to emit byte order mark, then reset the stream // so that it is written over. object checkpoint = utilWriter.CreateStreamCheckpoint(); utilWriter.Write('x'); utilWriter.RestoreStreamCheckpoint(checkpoint); } }
// // Copy the reader until we hit an element, or we've exited the current depth. // internal bool CopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { bool moreToRead = true; // Set the depth if we limit copying to this depth int depth; if (limitDepth) { // there is nothing in the element if (_reader.NodeType == XmlNodeType.EndElement) return true; depth = _reader.Depth; } else { depth = 0; } // Copy nodes until we've reached the desired depth, or until we hit an element. do { if (_reader.NodeType == XmlNodeType.Element) break; if (_reader.Depth < depth) { break; } moreToRead = CopyXmlNode(utilWriter); } while (moreToRead); return moreToRead; }
// Copy a config file, replacing sections with updates. private void CopyConfig(SectionUpdates declarationUpdates, ConfigDefinitionUpdates definitionUpdates, byte[] buffer, string filename, NamespaceChange namespaceChange, XmlUtilWriter utilWriter) { CheckPreamble(ConfigStreamInfo.StreamEncoding.GetPreamble(), utilWriter, buffer); using (Stream stream = new MemoryStream(buffer)) { using (XmlUtil xmlUtil = new XmlUtil(stream, filename, false)) { // copy up to the <configuration> node XmlTextReader reader = xmlUtil.Reader; reader.WhitespaceHandling = WhitespaceHandling.All; reader.Read(); xmlUtil.CopyReaderToNextElement(utilWriter, false); Debug.Assert(reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGURATION, "reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGURATION"); int indent = DEFAULT_INDENT; int configurationElementLinePosition = xmlUtil.TrueLinePosition; bool isEmptyConfigurationElement = reader.IsEmptyElement; // copy <configuration> node // if the node is an empty element, we may need to open it. string configurationStartElement; if (namespaceChange == NamespaceChange.Add) { configurationStartElement = string.Format( CultureInfo.InvariantCulture, FORMAT_CONFIGURATION_NAMESPACE, KEYWORD_CONFIGURATION_NAMESPACE); } else if (namespaceChange == NamespaceChange.Remove) { configurationStartElement = FORMAT_CONFIGURATION; } else { configurationStartElement = null; } bool needsChildren = (declarationUpdates != null || definitionUpdates != null); string configurationEndElement = xmlUtil.UpdateStartElement(utilWriter, configurationStartElement, needsChildren, configurationElementLinePosition, indent); bool foundConfigSectionsElement = false; if (!isEmptyConfigurationElement) { // copy up to the first element under <configuration> xmlUtil.CopyReaderToNextElement(utilWriter, true); // updateIndent indent = UpdateIndent(indent, xmlUtil, utilWriter, configurationElementLinePosition); if (reader.NodeType == XmlNodeType.Element && reader.Name == KEYWORD_CONFIGSECTIONS) { foundConfigSectionsElement = true; int configSectionsElementLinePosition = xmlUtil.TrueLinePosition; bool isEmptyConfigSectionsElement = reader.IsEmptyElement; // if no updates, copy the entire <configSections> element if (declarationUpdates == null) { xmlUtil.CopyOuterXmlToNextElement(utilWriter, true); } else { // copy <configSections>, and open it if it is an empty element string configSectionsEndElement = xmlUtil.UpdateStartElement( utilWriter, null, true, configSectionsElementLinePosition, indent); if (!isEmptyConfigSectionsElement) { // copy to next element under <configSections>, or up to closing </configSections> xmlUtil.CopyReaderToNextElement(utilWriter, true); // copy config declarations CopyConfigDeclarationsRecursive(declarationUpdates, xmlUtil, utilWriter, string.Empty, configSectionsElementLinePosition, indent); Debug.Assert(reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGSECTIONS, "reader.NodeType == XmlNodeType.EndElement && reader.Name == \"KEYWORD_CONFIGSECTIONS\""); } // write declarations not written by above copy if (declarationUpdates.HasUnretrievedSections()) { // determine the line position of the end element int endElementLinePosition = 0; if (configSectionsEndElement == null) { endElementLinePosition = xmlUtil.TrueLinePosition; } // indent a new line if (!utilWriter.IsLastLineBlank) { utilWriter.AppendNewLine(); } WriteUnwrittenConfigDeclarations(declarationUpdates, utilWriter, configSectionsElementLinePosition + indent, indent, false); // restore spaces to end element if (configSectionsEndElement == null) { utilWriter.AppendSpacesToLinePosition(endElementLinePosition); } } // Copy the </configSections> element if (configSectionsEndElement == null) { xmlUtil.CopyXmlNode(utilWriter); } else { // note that configSectionsEndElement already contains the proper indenting utilWriter.Write(configSectionsEndElement); } // copy up to the next element under <configuration>, or up to closing </configSections> xmlUtil.CopyReaderToNextElement(utilWriter, true); } } } // Write new declarations if (!foundConfigSectionsElement && declarationUpdates != null) { bool skipFirstIndent = reader.Depth > 0 && reader.NodeType == XmlNodeType.Element; int newConfigSectionsLinePosition; if (skipFirstIndent) { newConfigSectionsLinePosition = xmlUtil.TrueLinePosition; } else { newConfigSectionsLinePosition = configurationElementLinePosition + indent; } WriteNewConfigDeclarations(declarationUpdates, utilWriter, newConfigSectionsLinePosition, indent, skipFirstIndent); } if (definitionUpdates != null) { // // Copy sections recursively. In the file we copy we start out at // location path="." allowOverride="true" inheritInChildApps="true" // bool locationPathApplies = false; LocationUpdates locationUpdates = null; SectionUpdates sectionUpdates = null; if (!IsLocationConfig) { locationPathApplies = true; locationUpdates = definitionUpdates.FindLocationUpdates(OverrideModeSetting.LocationDefault, true); if (locationUpdates != null) { sectionUpdates = locationUpdates.SectionUpdates; } } CopyConfigDefinitionsRecursive(definitionUpdates, xmlUtil, utilWriter, locationPathApplies, locationUpdates, sectionUpdates, true, string.Empty, configurationElementLinePosition, indent); // Write new config sections from new groups. WriteNewConfigDefinitions(definitionUpdates, utilWriter, configurationElementLinePosition + indent, indent); #if DBG Debug.Assert(configurationEndElement != null || (reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGURATION), "configurationEndElement != null || (reader.NodeType == XmlNodeType.EndElement && reader.Name == KEYWORD_CONFIGURATION)"); #endif #if DBG { foreach (LocationUpdates l in definitionUpdates.LocationUpdatesList) { Debug.Assert(!l.SectionUpdates.HasUnretrievedSections(), "!l.SectionUpdates.HasUnretrievedSections()"); } } #endif } if (configurationEndElement != null) { // If we have to add closing config tag, then do it now // before copying extra whitespace/comments if (!utilWriter.IsLastLineBlank) { utilWriter.AppendNewLine(); } utilWriter.Write(configurationEndElement); } // // Copy the remainder of the file, the closing </configuration> node plus any whitespace // and comments // while (xmlUtil.CopyXmlNode(utilWriter)) { } } } }
// // Skip over the current element and copy until the next element. // This function removes the one blank line that would otherwise // be inserted by simply skipping and copying to the next element // in a situation like this: // // <!-- end of previous configSection --> // <configSectionToDelete> // <content /> // <moreContent /> // </configSectionToDelete> // <!-- end of configSectionToDelete --> // <nextConfigSection /> // internal bool SkipAndCopyReaderToNextElement(XmlUtilWriter utilWriter, bool limitDepth) { Debug.Assert(_reader.NodeType == XmlNodeType.Element, "_reader.NodeType == XmlNodeType.Element"); // If the last line before the element is not blank, then we do not have to // remove the blank line. if (!utilWriter.IsLastLineBlank) { _reader.Skip(); return CopyReaderToNextElement(utilWriter, limitDepth); } // Set the depth if we limit copying to this depth int depth; if (limitDepth) { depth = _reader.Depth; } else { depth = 0; } // Skip over the element _reader.Skip(); int lineNumberOfEndElement = _reader.LineNumber; // Read until we hit a a non-whitespace node or reach the end while (!_reader.EOF) { if (_reader.NodeType != XmlNodeType.Whitespace) { // // If the next non-whitepace node is on another line, // seek back to the beginning of the current blank line, // skip a blank line of whitespace, and copy the remaining whitespace. // if (_reader.LineNumber > lineNumberOfEndElement) { utilWriter.SeekToLineStart(); utilWriter.AppendWhiteSpace(lineNumberOfEndElement + 1, 1, LineNumber, TrueLinePosition); } break; } _reader.Read(); } // Copy nodes until we've reached the desired depth, or until we hit an element. while (!_reader.EOF) { if (_reader.NodeType == XmlNodeType.Element) break; if (_reader.Depth < depth) { break; } CopyXmlNode(utilWriter); }; return !_reader.EOF; }
// Copy configuration sections from the original configuration file. 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 = false; OverrideModeSetting overrideMode = OverrideModeSetting.LocationDefault; bool inheritInChildApps = true; if (IsLocationConfig) { // For location config we will compare config paths instead of location strings // so that we dont end up comparing "1" with "Default Web Site" and ending up with the wrong result if (locationSubPathAttribute == null) { elementLocationPathApplies = false; } else { elementLocationPathApplies = StringUtil.EqualsIgnoreCase(ConfigPath, Host.GetConfigPathFromLocationSubPath(Parent.ConfigPath, locationSubPathAttribute)); } } else { Debug.Assert(LocationSubPath == null); // This is the same as doing StringUtil.EqualsIgnoreCase(_locationSubPath, locationSubPathAttribute) // but remember the first one is null. Also remember locationSubPathAttribute is already normalized elementLocationPathApplies = (locationSubPathAttribute == null); } if (elementLocationPathApplies) { // Retrieve overrideMode and InheritInChildApps string allowOverrideAttribute = reader.GetAttribute(KEYWORD_LOCATION_ALLOWOVERRIDE); if (allowOverrideAttribute != null) { overrideMode = OverrideModeSetting.CreateFromXmlReadValue(Boolean.Parse(allowOverrideAttribute)); } string overrideModeAttribute = reader.GetAttribute(KEYWORD_LOCATION_OVERRIDEMODE); if (overrideModeAttribute != null) { overrideMode = OverrideModeSetting.CreateFromXmlReadValue(OverrideModeSetting.ParseOverrideModeXmlValue(overrideModeAttribute, null)); Debug.Assert(allowOverrideAttribute == null, "allowOverride and overrideMode both detected in a <location> tag"); } 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(overrideMode, 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(overrideMode, 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; }
// // Copy an XML element and its children, up to and including the end element. // private void CopyElement(XmlUtilWriter utilWriter) { Debug.Assert(_reader.NodeType== XmlNodeType.Element, "_reader.NodeType== XmlNodeType.Element"); int depth = _reader.Depth; bool isEmptyElement = _reader.IsEmptyElement; // Copy current node CopyXmlNode(utilWriter); // Copy nodes while the depth is greater than the current depth. while (_reader.Depth > depth) { CopyXmlNode(utilWriter); } // Copy the end element. if (!isEmptyElement) { CopyXmlNode(utilWriter); } }
// // SaveConfigSource // private void SaveConfigSource(DefinitionUpdate update) { string configSourceStreamName; if (update.SectionRecord.HasResult) { ConfigurationSection configSection = (ConfigurationSection) update.SectionRecord.Result; configSourceStreamName = configSection.SectionInformation.ConfigSourceStreamName; } else { Debug.Assert(update.SectionRecord.HasFileInput, "update.SectionRecord.HasFileInput"); SectionInput fileInput = update.SectionRecord.FileInput; configSourceStreamName = fileInput.SectionXmlInfo.ConfigSourceStreamName; } // Copy the input stream before opening the output stream. byte[] readBuffer = null; using (Stream streamRead = Host.OpenStreamForRead(configSourceStreamName)) { if (streamRead != null) { readBuffer = new byte[streamRead.Length]; int count = streamRead.Read(readBuffer, 0, (int) streamRead.Length); if (count != streamRead.Length) { throw new ConfigurationErrorsException(); } } } // Write the changes to the output stream. bool hasFile = (readBuffer != null); object writeContext = null; bool streamOpened = false; try { try { string templateStreamName; if (Host.IsRemote) { // templateStreamName is used by OpenStreamForWrite for copying file attributes during saving. // (for details, see WriteFileContext.Complete.) // // If we're using a remote host, then ConfigStreamInfo.StreamName is actually pointing to a // full filepath on a remote machine. In this case, it's impossible to copy the attributes // over, and thus we won't do it. templateStreamName = null; } else { templateStreamName = ConfigStreamInfo.StreamName; } using (Stream streamWrite = Host.OpenStreamForWrite(configSourceStreamName, templateStreamName, ref writeContext)) { streamOpened = true; if (update.UpdatedXml == null) { Debug.Assert(hasFile, "hasFile"); if (hasFile) { streamWrite.Write(readBuffer, 0, readBuffer.Length); } } else { using (StreamWriter streamWriter = new StreamWriter(streamWrite)) { XmlUtilWriter utilWriter = new XmlUtilWriter(streamWriter, true); if (hasFile) { CopyConfigSource(utilWriter, update.UpdatedXml, configSourceStreamName, readBuffer); } else { CreateNewConfigSource(utilWriter, update.UpdatedXml, DEFAULT_INDENT); } } } } } catch { if (streamOpened) { Host.WriteCompleted(configSourceStreamName, false, writeContext); } throw; } } // // Guarantee that exceptions contain at least the name of the stream by wrapping them // in a ConfigurationException. // catch (Exception e) { throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, configSourceStreamName, 0); } Host.WriteCompleted(configSourceStreamName, true, writeContext); }
// // Copy a single XML node, attempting to preserve whitespace. // A side effect of this method is to advance the reader to the next node. // // PERFORMANCE NOTE: this function is used at runtime to copy a configuration section, // and at designtime to copy an entire XML document. // // At designtime, this function needs to be able to copy a <!DOCTYPE declaration. // Copying a <!DOCTYPE declaration is expensive, because due to limitations of the // XmlReader API, we must track the position of the writer to accurately format it. // Tracking the position of the writer is expensive, as it requires examining every // character that is written for newline characters, and maintaining the seek position // of the underlying stream at each new line, which in turn requires a stream flush. // // This function must NEVER require tracking the writer position to copy the Xml nodes // that are used in a configuration section. // internal bool CopyXmlNode(XmlUtilWriter utilWriter) { // // For nodes that have a closing string, such as "<element >" // the XmlReader API does not give us the location of the closing string, e.g. ">". // To correctly determine the location of the closing part, we advance the reader, // determine the position of the next node, then work backwards to add whitespace // and add the closing string. // string close = null; int lineNumber = -1; int linePosition = -1; int readerLineNumber = 0; int readerLinePosition = 0; int writerLineNumber = 0; int writerLinePosition = 0; if (utilWriter.TrackPosition) { readerLineNumber = _reader.LineNumber; readerLinePosition = _reader.LinePosition; writerLineNumber = utilWriter.LineNumber; writerLinePosition = utilWriter.LinePosition; } // We test the node type in the likely order of decreasing occurrence. XmlNodeType nodeType = _reader.NodeType; if (nodeType == XmlNodeType.Whitespace) { utilWriter.Write(_reader.Value); } else if (nodeType == XmlNodeType.Element) { close = (_reader.IsEmptyElement) ? "/>" : ">"; // get the line position after the element declaration: // <element attr="value" // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; utilWriter.Write('<'); utilWriter.Write(_reader.Name); // // Note that there is no way to get spacing between attribute name and value // For example: // // <elem attr="value" /> // // is reported with the same position as // // <elem attr = "value" /> // // The first example has no spaces around '=', the second example does. // while (_reader.MoveToNextAttribute()) { // get line position of the attribute declaration // <element attr="value" // ^ // attrLinePosition // int attrLineNumber = _reader.LineNumber; int attrLinePosition = _reader.LinePosition; // Write the whitespace before the attribute utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, attrLineNumber, attrLinePosition); // Write the attribute and value int charactersWritten = utilWriter.Write(_reader.Name); charactersWritten += utilWriter.Write('='); charactersWritten += utilWriter.AppendAttributeValue(_reader); // Update position. Note that the attribute value is escaped to always be on a single line. lineNumber = attrLineNumber; linePosition = attrLinePosition + charactersWritten; } } else if (nodeType == XmlNodeType.EndElement) { close = ">"; // get line position after the end element declaration: // </element > // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; utilWriter.Write("</"); utilWriter.Write(_reader.Name); } else if (nodeType == XmlNodeType.Comment) { utilWriter.AppendComment(_reader.Value); } else if (nodeType == XmlNodeType.Text) { utilWriter.AppendEscapeTextString(_reader.Value); } else if (nodeType == XmlNodeType.XmlDeclaration) { close = "?>"; // get line position after the xml declaration: // <?xml version="1.0" // ^ // linePosition // lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + 3; utilWriter.Write("<?xml"); // // Note that there is no way to get spacing between attribute name and value // For example: // // <?xml attr="value" ?> // // is reported with the same position as // // <?xml attr = "value" ?> // // The first example has no spaces around '=', the second example does. // while (_reader.MoveToNextAttribute()) { // get line position of the attribute declaration // <?xml version="1.0" // ^ // attrLinePosition // int attrLineNumber = _reader.LineNumber; int attrLinePosition = _reader.LinePosition; // Write the whitespace before the attribute utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, attrLineNumber, attrLinePosition); // Write the attribute and value int charactersWritten = utilWriter.Write(_reader.Name); charactersWritten += utilWriter.Write('='); charactersWritten += utilWriter.AppendAttributeValue(_reader); // Update position. Note that the attribute value is escaped to always be on a single line. lineNumber = attrLineNumber; linePosition = attrLinePosition + charactersWritten; } // Position reader at beginning of node _reader.MoveToElement(); } else if (nodeType == XmlNodeType.SignificantWhitespace) { utilWriter.Write(_reader.Value); } else if (nodeType == XmlNodeType.ProcessingInstruction) { // // Note that there is no way to get spacing between attribute name and value // For example: // // <?pi "value" ?> // // is reported with the same position as // // <?pi "value" ?> // // The first example has one space between 'pi' and "value", the second has multiple spaces. // utilWriter.AppendProcessingInstruction(_reader.Name, _reader.Value); } else if (nodeType == XmlNodeType.EntityReference) { utilWriter.AppendEntityRef(_reader.Name); } else if (nodeType == XmlNodeType.CDATA) { utilWriter.AppendCData(_reader.Value); } else if (nodeType == XmlNodeType.DocumentType) { // // XmlNodeType.DocumentType has the following format: // // <!DOCTYPE rootElementName {(SYSTEM uriRef)|(PUBLIC id uriRef)} {[ dtdDecls ]} > // // The reader only gives us the position of 'rootElementName', so we must track what was // written before "<!DOCTYPE" in order to correctly determine the position of the // <!DOCTYPE tag // Debug.Assert(utilWriter.TrackPosition, "utilWriter.TrackPosition"); int c = utilWriter.Write("<!DOCTYPE"); // Write the space between <!DOCTYPE and the rootElementName utilWriter.AppendRequiredWhiteSpace(_lastLineNumber, _lastLinePosition + c, _reader.LineNumber, _reader.LinePosition); // Write the rootElementName utilWriter.Write(_reader.Name); // Get the dtd declarations, if any string dtdValue = null; if (_reader.HasValue) { dtdValue = _reader.Value; } // get line position after the !DOCTYPE declaration: // <!DOCTYPE rootElement SYSTEM rootElementDtdUri > // ^ // linePosition lineNumber = _reader.LineNumber; linePosition = _reader.LinePosition + _reader.Name.Length; // Note that there is no way to get the spacing after PUBLIC or SYSTEM attributes and their values if (_reader.MoveToFirstAttribute()) { // Write the space before SYSTEM or PUBLIC utilWriter.AppendRequiredWhiteSpace(lineNumber, linePosition, _reader.LineNumber, _reader.LinePosition); // Write SYSTEM or PUBLIC and the 1st value of the attribute string attrName = _reader.Name; utilWriter.Write(attrName); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(_reader); _reader.MoveToAttribute(0); // If PUBLIC, write the second value of the attribute if (attrName == "PUBLIC") { _reader.MoveToAttribute(1); utilWriter.AppendSpace(); utilWriter.AppendAttributeValue(_reader); _reader.MoveToAttribute(1); } } // If there is a dtd, write it if (dtdValue != null && dtdValue.Length > 0) { utilWriter.Write(" ["); utilWriter.Write(dtdValue); utilWriter.Write(']'); } utilWriter.Write('>'); } // Advance the _reader so we can get the position of the next node. bool moreToRead = _reader.Read(); nodeType = _reader.NodeType; // Close the node we are copying. if (close != null) { // // Find the position of the close string, for example: // // <element > <subElement /> // ^ // closeLinePosition // int startOffset = GetPositionOffset(nodeType); int closeLineNumber = _reader.LineNumber; int closeLinePosition = _reader.LinePosition - startOffset - close.Length; // Add whitespace up to the position of the close string utilWriter.AppendWhiteSpace(lineNumber, linePosition, closeLineNumber, closeLinePosition); // Write the close string utilWriter.Write(close); } // // Track the position of the reader based on the position of the reader // before we copied this node and what we have written in copying the node. // This allows us to determine the position of the <!DOCTYPE tag. // if (utilWriter.TrackPosition) { _lastLineNumber = (readerLineNumber - writerLineNumber) + utilWriter.LineNumber; if (writerLineNumber == utilWriter.LineNumber) { _lastLinePosition = (readerLinePosition - writerLinePosition) + utilWriter.LinePosition; } else { _lastLinePosition = utilWriter.LinePosition; } } return moreToRead; }
private void CreateNewConfigSource(XmlUtilWriter utilWriter, string updatedXml, int indent) { string formattedXml = XmlUtil.FormatXmlElement(updatedXml, 0, indent, true); utilWriter.Write(string.Format(CultureInfo.InvariantCulture, FORMAT_CONFIGSOURCE_FILE, ConfigStreamInfo.StreamEncoding.WebName)); utilWriter.Write(formattedXml + NL); }
// // Copy or replace an element node. // If the element is an empty element, replace it with a formatted start element if either: // * The contents of the start element string need updating. // * The element needs to contain child elements. // // If the element is empty and is replaced with a start/end element pair, return a // end element string with whitespace formatting; otherwise return null. // internal string UpdateStartElement(XmlUtilWriter utilWriter, string updatedStartElement, bool needsChildren, int linePosition, int indent) { Debug.Assert(_reader.NodeType == XmlNodeType.Element, "_reader.NodeType == NodeType.Element"); string endElement = null; bool needsEndElement = false; string elementName; elementName = _reader.Name; // If the element is empty, determine if a new end element is needed. if (_reader.IsEmptyElement) { if (updatedStartElement == null && needsChildren) { updatedStartElement = RetrieveFullOpenElementTag(); } needsEndElement = (updatedStartElement != null); } if (updatedStartElement == null) { // // If no changes to the start element are required, just copy it. // CopyXmlNode(utilWriter); } else { // // Format a new start element/end element pair // string updatedEndElement = "</" + elementName + ">"; string updatedElement = updatedStartElement + updatedEndElement; string formattedElement = FormatXmlElement(updatedElement, linePosition, indent, true); // // Get the start and end element strings from the formatted element. // int iEndElement = formattedElement.LastIndexOf('\n') + 1; string startElement; if (needsEndElement) { endElement = formattedElement.Substring(iEndElement); // Include a newline in the start element as we are expanding an empty element. startElement = formattedElement.Substring(0, iEndElement); } else { // Omit the newline from the start element. startElement = formattedElement.Substring(0, iEndElement - 2); } // Write the new start element. utilWriter.Write(startElement); // Skip over the existing start element. _reader.Read(); } return endElement; }
// // Copy an XML element but skip all its child elements, then continue copying until we've hit the next element. // internal bool SkipChildElementsAndCopyOuterXmlToNextElement(XmlUtilWriter utilWriter) { bool isEmptyElement = _reader.IsEmptyElement; int startingLine = _reader.LineNumber; #if DBG int depth = _reader.Depth; #endif Debug.Assert(_reader.NodeType == XmlNodeType.Element, "_reader.NodeType == XmlNodeType.Element"); CopyXmlNode(utilWriter); // See if we need to skip any child element if (!isEmptyElement) { while (_reader.NodeType != XmlNodeType.EndElement) { // Skip all the inner child elements if (_reader.NodeType == XmlNodeType.Element) { _reader.Skip(); // We need to skip all the whitespaces following a skipped element. // - If the whitespaces don't contain /r/n, then it's okay to skip them // as part of the element. // - If the whitespaces contain /r/n, not skipping them will result // in a redundant emtpy line being copied. if (_reader.NodeType == XmlNodeType.Whitespace) { _reader.Skip(); } } else { // We want to preserve other content, e.g. comments. CopyXmlNode(utilWriter); } } if (_reader.LineNumber != startingLine) { // The whitespace in front of the EndElement was skipped above. // We need to append spaces to compensate for that. utilWriter.AppendSpacesToLinePosition(TrueLinePosition); } #if DBG Debug.Assert(_reader.Depth == depth, "We should be at the same depth as the opening Element"); #endif // Copy the end element. CopyXmlNode(utilWriter); } return CopyReaderToNextElement(utilWriter, true); }
internal string UpdateStartElement(XmlUtilWriter utilWriter, string updatedStartElement, bool needsChildren, int linePosition, int indent) { string str = null; string str6; bool flag = false; string name = this._reader.Name; if (this._reader.IsEmptyElement) { if ((updatedStartElement == null) && needsChildren) { updatedStartElement = this.RetrieveFullOpenElementTag(); } flag = updatedStartElement != null; } if (updatedStartElement == null) { this.CopyXmlNode(utilWriter); return str; } string str5 = FormatXmlElement(updatedStartElement + ("</" + name + ">"), linePosition, indent, true); int startIndex = str5.LastIndexOf('\n') + 1; if (flag) { str = str5.Substring(startIndex); str6 = str5.Substring(0, startIndex); } else { str6 = str5.Substring(0, startIndex - 2); } utilWriter.Write(str6); this._reader.Read(); return str; }