private void ProcessAttributes( XmlReader xmlReader, StringBuilder output, ElementProcessContext elementProcessContext, bool isNoLineBreakElement, string attributeIndentationString) { var list = new List <AttributeInfo>(xmlReader.AttributeCount); var firstLineList = new List <AttributeInfo>(xmlReader.AttributeCount); while (xmlReader.MoveToNextAttribute()) { var attributeInfo = this.attributeInfoFactory.Create(xmlReader); list.Add(attributeInfo); // Maintain separate list of first line attributes. if (this.options.EnableAttributeReordering && this.IsFirstLineAttribute(attributeInfo.Name)) { firstLineList.Add(attributeInfo); } // Check for xml:space as defined in http://www.w3.org/TR/2008/REC-xml-20081126/#sec-white-space if (xmlReader.IsXmlSpaceAttribute()) { elementProcessContext.Current.IsPreservingSpace = (xmlReader.Value == "preserve"); } } if (this.options.EnableAttributeReordering) { list.Sort(this.AttributeInfoComparison); firstLineList.Sort(this.AttributeInfoComparison); } var noLineBreakInAttributes = (list.Count <= this.options.AttributesTolerance) || isNoLineBreakElement; var forceLineBreakInAttributes = false; // Root element? if (elementProcessContext.Count == 2) { switch (this.options.RootElementLineBreakRule) { case LineBreakRule.Default: break; case LineBreakRule.Always: noLineBreakInAttributes = false; forceLineBreakInAttributes = true; break; case LineBreakRule.Never: noLineBreakInAttributes = true; break; default: throw new ArgumentOutOfRangeException(); } } // No need to break attributes. if (noLineBreakInAttributes) { foreach (var attrInfo in list) { output.Append(' ').Append(this.attributeInfoFormatter.ToSingleLineString(attrInfo)); } elementProcessContext.Current.IsMultlineStartTag = false; } else { // Need to break attributes. var attributeLines = new List <string>(); var currentLineBuffer = new StringBuilder(); int attributeCountInCurrentLineBuffer = 0; AttributeInfo lastAttributeInfo = null; // Process first line attributes. string firstLine = String.Empty; foreach (var attrInfo in firstLineList) { firstLine = $"{firstLine} {this.attributeInfoFormatter.ToSingleLineString(attrInfo)}"; } if (firstLine.Length > 0) { attributeLines.Add(firstLine); } foreach (AttributeInfo attrInfo in list) { // Skip attributes already added to first line. if (firstLineList.Contains(attrInfo)) { continue; } // Attributes with markup extension, always put on new line if (attrInfo.IsMarkupExtension && this.options.FormatMarkupExtension) { if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } attributeLines.Add( this.attributeInfoFormatter.ToMultiLineString(attrInfo, attributeIndentationString)); } else { string pendingAppend = this.attributeInfoFormatter.ToSingleLineString(attrInfo); bool isAttributeCharLengthExceeded = (attributeCountInCurrentLineBuffer > 0) && (this.options.MaxAttributeCharactersPerLine > 0) && ((currentLineBuffer.Length + pendingAppend.Length) > this.options.MaxAttributeCharactersPerLine); bool isAttributeCountExceeded = (this.options.MaxAttributesPerLine > 0) && ((attributeCountInCurrentLineBuffer + 1) > this.options.MaxAttributesPerLine); bool isAttributeRuleGroupChanged = this.options.PutAttributeOrderRuleGroupsOnSeparateLines && (lastAttributeInfo != null) && (lastAttributeInfo.OrderRule.Group != attrInfo.OrderRule.Group); if ((currentLineBuffer.Length > 0) && (forceLineBreakInAttributes || isAttributeCharLengthExceeded || isAttributeCountExceeded || isAttributeRuleGroupChanged)) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } currentLineBuffer.AppendFormat("{0} ", pendingAppend); attributeCountInCurrentLineBuffer++; } lastAttributeInfo = attrInfo; } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); } for (int i = 0; i < attributeLines.Count; i++) { // Put first attribute line on same line as element? if ((i == 0) && (this.options.KeepFirstAttributeOnSameLine || (firstLineList.Count > 0))) { output.Append(' ').Append(attributeLines[i].Trim()); } else { output.Append(Environment.NewLine) .Append(this.indentService.Normalize(attributeIndentationString + attributeLines[i].Trim())); } } elementProcessContext.Current.IsMultlineStartTag = true; } }
private void ProcessAttributes( XmlReader xmlReader, StringBuilder output, ElementProcessContext elementProcessContext, bool isNoLineBreakElement, string attributeIndentationString) { var list = new List <AttributeInfo>(xmlReader.AttributeCount); var firstLineList = new List <AttributeInfo>(xmlReader.AttributeCount); while (xmlReader.MoveToNextAttribute()) { var attributeInfo = this.attributeInfoFactory.Create(xmlReader); list.Add(attributeInfo); // Maintain separate list of first line attributes. if (this.options.EnableAttributeReordering && this.IsFirstLineAttribute(attributeInfo.Name)) { firstLineList.Add(attributeInfo); } // Check for xml:space as defined in http://www.w3.org/TR/2008/REC-xml-20081126/#sec-white-space if (xmlReader.IsXmlSpaceAttribute()) { elementProcessContext.Current.IsPreservingSpace = (xmlReader.Value == "preserve"); } } if (this.options.EnableAttributeReordering) { // .NET performs insertion sort if collection partition size is fewer than 16 elements, but it uses // Heapsort or Quicksort under different conditions. This can lead to an unstable sort and randomized // attributbes while formatting. Even though insertion sort is less performant, XAML elements with more // than 16 attributes are not common, so the effect of forcing insertion sort is negligable in all but // the most extreme of cases. - https://msdn.microsoft.com/en-us/library/b0zbh7b6(v=vs.110).aspx list.InsertionSort(this.AttributeInfoComparison); firstLineList.InsertionSort(this.AttributeInfoComparison); } var noLineBreakInAttributes = (list.Count <= this.options.AttributesTolerance) || isNoLineBreakElement; var forceLineBreakInAttributes = false; // Root element? if (elementProcessContext.Count == 2) { switch (this.options.RootElementLineBreakRule) { case LineBreakRule.Default: break; case LineBreakRule.Always: noLineBreakInAttributes = false; forceLineBreakInAttributes = true; break; case LineBreakRule.Never: noLineBreakInAttributes = true; break; default: throw new NotImplementedException(); } } // No need to break attributes. if (noLineBreakInAttributes) { foreach (var attrInfo in list) { var ignoreEncode = IsIgnoreEncodeForAttributes(attrInfo.Name); output.Append(' ').Append(this.attributeInfoFormatter.ToSingleLineString(attrInfo, ignoreEncode)); } elementProcessContext.Current.IsMultlineStartTag = false; } else { // Need to break attributes. var attributeLines = new List <string>(); var currentLineBuffer = new StringBuilder(); int attributeCountInCurrentLineBuffer = 0; int xmlnsAliasesBypassLengthInCurrentLine = 0; AttributeInfo lastAttributeInfo = null; // Process first line attributes. string firstLine = String.Empty; foreach (var attrInfo in firstLineList) { var ignoreEncode = IsIgnoreEncodeForAttributes(attrInfo.Name); firstLine = $"{firstLine} {this.attributeInfoFormatter.ToSingleLineString(attrInfo, ignoreEncode)}"; } if (firstLine.Length > 0) { attributeLines.Add(firstLine); } foreach (AttributeInfo attrInfo in list) { // Skip attributes already added to first line. if (firstLineList.Contains(attrInfo)) { continue; } // Attributes with markup extension, always put on new line if (attrInfo.IsMarkupExtension && this.options.FormatMarkupExtension) { if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } attributeLines.Add( this.attributeInfoFormatter.ToMultiLineString(attrInfo, attributeIndentationString)); } else { var ignoreEncode = IsIgnoreEncodeForAttributes(attrInfo.Name); string pendingAppend = this.attributeInfoFormatter.ToSingleLineString(attrInfo, ignoreEncode); var actualPendingAppend = this.xmlEscapingService.RestoreXmlnsAliasesBypass(pendingAppend); xmlnsAliasesBypassLengthInCurrentLine += pendingAppend.Length - actualPendingAppend.Length; bool isAttributeCharLengthExceeded = (attributeCountInCurrentLineBuffer > 0) && (this.options.MaxAttributeCharactersPerLine > 0) && ((currentLineBuffer.Length + pendingAppend.Length - xmlnsAliasesBypassLengthInCurrentLine) > this.options.MaxAttributeCharactersPerLine); bool isAttributeCountExceeded = (this.options.MaxAttributesPerLine > 0) && ((attributeCountInCurrentLineBuffer + 1) > this.options.MaxAttributesPerLine); bool isAttributeRuleGroupChanged = this.options.PutAttributeOrderRuleGroupsOnSeparateLines && (lastAttributeInfo != null) && (lastAttributeInfo.OrderRule.Group != attrInfo.OrderRule.Group); if ((currentLineBuffer.Length > 0) && (forceLineBreakInAttributes || isAttributeCharLengthExceeded || isAttributeCountExceeded || isAttributeRuleGroupChanged)) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; xmlnsAliasesBypassLengthInCurrentLine = 0; } currentLineBuffer.AppendFormat(CultureInfo.InvariantCulture, "{0} ", pendingAppend); attributeCountInCurrentLineBuffer++; xmlnsAliasesBypassLengthInCurrentLine += pendingAppend.Length - actualPendingAppend.Length; } lastAttributeInfo = attrInfo; } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); } for (int i = 0; i < attributeLines.Count; i++) { // Put first attribute line on same line as element? if ((i == 0) && (this.options.KeepFirstAttributeOnSameLine || (firstLineList.Count > 0))) { output.Append(' ').Append(attributeLines[i].Trim()); } else { output.Append(Environment.NewLine) .Append(this.indentService.Normalize(attributeIndentationString + attributeLines[i].Trim())); } } elementProcessContext.Current.IsMultlineStartTag = true; } }
private void ProcessElement(XmlReader xmlReader, StringBuilder output) { string currentIndentString = GetIndentString(xmlReader.Depth); string elementName = xmlReader.Name; // Calculate how element should be indented if (!this.ElementProcessStatusStack.Peek().IsPreservingSpace) { // "Run" get special treatment to try to preserve spacing. Use xml:space='preserve' to make sure! if (elementName.Equals("Run")) { this.ElementProcessStatusStack.Peek().Parent.IsSignificantWhiteSpace = true; if ((output.Length == 0) || output.IsNewLine()) { output.Append(currentIndentString); } } else { this.ElementProcessStatusStack.Peek().Parent.IsSignificantWhiteSpace = false; if ((output.Length == 0) || output.IsNewLine()) { output.Append(currentIndentString); } else { output.Append(Environment.NewLine).Append(currentIndentString); } } } // Output the element itself output.Append('<').Append(xmlReader.Name); bool isEmptyElement = xmlReader.IsEmptyElement; bool hasPutEndingBracketOnNewLine = false; var list = new List<AttributeInfo>(xmlReader.AttributeCount); if (xmlReader.HasAttributes) { while (xmlReader.MoveToNextAttribute()) { string attributeName = xmlReader.Name; string attributeValue = xmlReader.Value; AttributeOrderRule orderRule = OrderRules.GetRuleFor(attributeName); list.Add(new AttributeInfo(attributeName, attributeValue, orderRule)); // Check for xml:space as defined in http://www.w3.org/TR/2008/REC-xml-20081126/#sec-white-space if (xmlReader.IsXmlSpaceAttribute()) { this.ElementProcessStatusStack.Peek().IsPreservingSpace = (xmlReader.Value == "preserve"); } } if (this.Options.EnableAttributeReordering) { list.Sort(AttributeInfoComparison); } currentIndentString = this.GetIndentString(xmlReader.Depth); var noLineBreakInAttributes = (list.Count <= this.Options.AttributesTolerance) || this.IsNoLineBreakElement(elementName); var forceLineBreakInAttributes = false; // Root element? if (this.ElementProcessStatusStack.Count == 2) { switch (this.Options.RootElementLineBreakRule) { case LineBreakRule.Default: break; case LineBreakRule.Always: noLineBreakInAttributes = false; forceLineBreakInAttributes = true; break; case LineBreakRule.Never: noLineBreakInAttributes = true; break; default: throw new ArgumentOutOfRangeException(); } } // No need to break attributes if (noLineBreakInAttributes) { foreach (var attrInfo in list) { output.Append(' ').Append(attrInfo.ToSingleLineString()); } this.ElementProcessStatusStack.Peek().IsMultlineStartTag = false; } // Need to break attributes else { IList<String> attributeLines = new List<String>(); var currentLineBuffer = new StringBuilder(); int attributeCountInCurrentLineBuffer = 0; AttributeInfo lastAttributeInfo = null; foreach (AttributeInfo attrInfo in list) { // Attributes with markup extension, always put on new line if (attrInfo.IsMarkupExtension && this.Options.FormatMarkupExtension) { string baseIndetationString; if (!this.Options.KeepFirstAttributeOnSameLine) { baseIndetationString = this.GetIndentString(xmlReader.Depth); } else { baseIndetationString = this.GetIndentString(xmlReader.Depth - 1) + String.Empty.PadLeft(elementName.Length + 2, ' '); } string pendingAppend; if (this.NoNewLineMarkupExtensionsList.Contains(attrInfo.MarkupExtension)) { pendingAppend = $" {attrInfo.ToSingleLineString()}"; } else { pendingAppend = attrInfo.ToMultiLineString(baseIndetationString); } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } attributeLines.Add(pendingAppend); } else { string pendingAppend = attrInfo.ToSingleLineString(); bool isAttributeCharLengthExceeded = (attributeCountInCurrentLineBuffer > 0) && (Options.MaxAttributeCharatersPerLine > 0) && ((currentLineBuffer.Length + pendingAppend.Length) > Options.MaxAttributeCharatersPerLine); bool isAttributeCountExceeded = (Options.MaxAttributesPerLine > 0) && ((attributeCountInCurrentLineBuffer + 1) > Options.MaxAttributesPerLine); bool isAttributeRuleGroupChanged = Options.SeparateByGroups && (lastAttributeInfo != null) && (lastAttributeInfo.OrderRule.Group != attrInfo.OrderRule.Group); if ((currentLineBuffer.Length > 0) && (forceLineBreakInAttributes || isAttributeCharLengthExceeded || isAttributeCountExceeded || isAttributeRuleGroupChanged)) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } currentLineBuffer.AppendFormat("{0} ", pendingAppend); attributeCountInCurrentLineBuffer++; } lastAttributeInfo = attrInfo; } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); } for (int i = 0; i < attributeLines.Count; i++) { if ((i == 0) && this.Options.KeepFirstAttributeOnSameLine) { output.Append(' ').Append(attributeLines[i].Trim()); // Align subsequent attributes with first attribute currentIndentString = GetIndentString(xmlReader.Depth - 1) + String.Empty.PadLeft(elementName.Length + 2, ' '); continue; } output.Append(Environment.NewLine).Append(currentIndentString).Append(attributeLines[i].Trim()); } this.ElementProcessStatusStack.Peek().IsMultlineStartTag = true; } // Determine if to put ending bracket on new line if (this.Options.PutEndingBracketOnNewLine && this.ElementProcessStatusStack.Peek().IsMultlineStartTag) { output.Append(Environment.NewLine).Append(currentIndentString); hasPutEndingBracketOnNewLine = true; } } if (isEmptyElement) { if (!hasPutEndingBracketOnNewLine && this.Options.SpaceBeforeClosingSlash) { output.Append(' '); } output.Append("/>"); this.ElementProcessStatusStack.Peek().IsSelfClosingElement = true; } else { output.Append(">"); } }
private void ProcessElement(XmlReader xmlReader, StringBuilder output) { string currentIndentString = GetIndentString(xmlReader.Depth); string elementName = xmlReader.Name; // Calculate how element should be indented if (!_elementProcessStatusStack.Peek().IsPreservingSpace) { // "Run" get special treatment to try to preserve spacing. Use xml:space='preserve' to make sure! if (elementName.Equals("Run")) { _elementProcessStatusStack.Peek().Parent.IsSignificantWhiteSpace = true; if (output.Length == 0 || output.IsNewLine()) { output.Append(currentIndentString); } } else { _elementProcessStatusStack.Peek().Parent.IsSignificantWhiteSpace = false; if (output.Length == 0 || output.IsNewLine()) { output.Append(currentIndentString); } else { output .Append(Environment.NewLine) .Append(currentIndentString); } } } // Output the element itself output .Append('<') .Append(xmlReader.Name); bool isEmptyElement = xmlReader.IsEmptyElement; bool hasPutEndingBracketOnNewLine = false; var list = new List <AttributeInfo>(xmlReader.AttributeCount); if (xmlReader.HasAttributes) { while (xmlReader.MoveToNextAttribute()) { string attributeName = xmlReader.Name; string attributeValue = xmlReader.Value; AttributeOrderRule orderRule = OrderRules.GetRuleFor(attributeName); list.Add(new AttributeInfo(attributeName, attributeValue, orderRule)); // Check for xml:space as defined in http://www.w3.org/TR/2008/REC-xml-20081126/#sec-white-space if (xmlReader.IsXmlSpaceAttribute()) { _elementProcessStatusStack.Peek().IsPreservingSpace = (xmlReader.Value == "preserve"); } } if (Options.EnableAttributeReordering) { list.Sort(AttributeInfoComparison); } currentIndentString = GetIndentString(xmlReader.Depth); var noLineBreakInAttributes = (list.Count <= Options.AttributesTolerance) || IsNoLineBreakElement(elementName); var forceLineBreakInAttributes = false; // Root element? if (_elementProcessStatusStack.Count == 2) { switch (Options.RootElementLineBreakRule) { case LineBreakRule.Default: break; case LineBreakRule.Always: noLineBreakInAttributes = false; forceLineBreakInAttributes = true; break; case LineBreakRule.Never: noLineBreakInAttributes = true; break; default: throw new ArgumentOutOfRangeException(); } } // No need to break attributes if (noLineBreakInAttributes) { foreach (var attrInfo in list) { output .Append(' ') .Append(attrInfo.ToSingleLineString()); } _elementProcessStatusStack.Peek().IsMultlineStartTag = false; } // Need to break attributes else { IList <String> attributeLines = new List <String>(); var currentLineBuffer = new StringBuilder(); int attributeCountInCurrentLineBuffer = 0; AttributeInfo lastAttributeInfo = null; foreach (AttributeInfo attrInfo in list) { // Attributes with markup extension, always put on new line if (attrInfo.IsMarkupExtension && Options.FormatMarkupExtension) { string baseIndetationString; if (!Options.KeepFirstAttributeOnSameLine) { baseIndetationString = GetIndentString(xmlReader.Depth); } else { baseIndetationString = GetIndentString(xmlReader.Depth - 1) + string.Empty.PadLeft(elementName.Length + 2, ' '); } string pendingAppend; if (NoNewLineMarkupExtensionsList.Contains(attrInfo.MarkupExtension)) { pendingAppend = " " + attrInfo.ToSingleLineString(); } else { pendingAppend = attrInfo.ToMultiLineString(baseIndetationString); } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } attributeLines.Add(pendingAppend); } else { string pendingAppend = attrInfo.ToSingleLineString(); bool isAttributeCharLengthExceeded = (attributeCountInCurrentLineBuffer > 0 && Options.MaxAttributeCharatersPerLine > 0 && currentLineBuffer.Length + pendingAppend.Length > Options.MaxAttributeCharatersPerLine); bool isAttributeCountExceeded = (Options.MaxAttributesPerLine > 0 && attributeCountInCurrentLineBuffer + 1 > Options.MaxAttributesPerLine); bool isAttributeRuleGroupChanged = Options.PutAttributeOrderRuleGroupsOnSeparateLines && lastAttributeInfo != null && lastAttributeInfo.OrderRule.Group != attrInfo.OrderRule.Group; if (currentLineBuffer.Length > 0 && (forceLineBreakInAttributes || isAttributeCharLengthExceeded || isAttributeCountExceeded || isAttributeRuleGroupChanged)) { attributeLines.Add(currentLineBuffer.ToString()); currentLineBuffer.Length = 0; attributeCountInCurrentLineBuffer = 0; } currentLineBuffer.AppendFormat("{0} ", pendingAppend); attributeCountInCurrentLineBuffer++; } lastAttributeInfo = attrInfo; } if (currentLineBuffer.Length > 0) { attributeLines.Add(currentLineBuffer.ToString()); } for (int i = 0; i < attributeLines.Count; i++) { if (0 == i && Options.KeepFirstAttributeOnSameLine) { output .Append(' ') .Append(attributeLines[i].Trim()); // Align subsequent attributes with first attribute currentIndentString = GetIndentString(xmlReader.Depth - 1) + String.Empty.PadLeft(elementName.Length + 2, ' '); continue; } output .Append(Environment.NewLine) .Append(currentIndentString) .Append(attributeLines[i].Trim()); } _elementProcessStatusStack.Peek().IsMultlineStartTag = true; } // Determine if to put ending bracket on new line if (Options.PutEndingBracketOnNewLine && _elementProcessStatusStack.Peek().IsMultlineStartTag) { output .Append(Environment.NewLine) .Append(currentIndentString); hasPutEndingBracketOnNewLine = true; } } if (isEmptyElement) { if (hasPutEndingBracketOnNewLine == false && Options.SpaceBeforeClosingSlash) { output.Append(' '); } output.Append("/>"); _elementProcessStatusStack.Peek().IsSelfClosingElement = true; } else { output.Append(">"); } }