Skip to content

Commit 8847075

Browse files
committed
Clean XmlHelper
1 parent 1c63d12 commit 8847075

File tree

1 file changed

+28
-242
lines changed

1 file changed

+28
-242
lines changed

src/PortToTripleSlash/src/libraries/XmlHelper.cs

Lines changed: 28 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Collections.Generic;
65
using System.Text.RegularExpressions;
76
using System.Xml;
87
using System.Xml.Linq;
@@ -11,78 +10,14 @@ namespace ApiDocsSync.PortToTripleSlash
1110
{
1211
internal class XmlHelper
1312
{
14-
private static readonly Dictionary<string, string> _replaceableNormalElementPatterns = new Dictionary<string, string> {
15-
{ "<c>null</c>", "<see langword=\"null\" />"},
16-
{ "<c>true</c>", "<see langword=\"true\" />"},
17-
{ "<c>false</c>", "<see langword=\"false\" />"},
18-
{ " null ", " <see langword=\"null\" /> " },
19-
{ " true ", " <see langword=\"true\" /> " },
20-
{ " false ", " <see langword=\"false\" /> " },
21-
{ " null,", " <see langword=\"null\" />," },
22-
{ " true,", " <see langword=\"true\" />," },
23-
{ " false,", " <see langword=\"false\" />," },
24-
{ " null.", " <see langword=\"null\" />." },
25-
{ " true.", " <see langword=\"true\" />." },
26-
{ " false.", " <see langword=\"false\" />." },
27-
{ "null ", "<see langword=\"null\" /> " },
28-
{ "true ", "<see langword=\"true\" /> " },
29-
{ "false ", "<see langword=\"false\" /> " },
30-
{ "Null ", "<see langword=\"null\" /> " },
31-
{ "True ", "<see langword=\"true\" /> " },
32-
{ "False ", "<see langword=\"false\" /> " },
33-
{ "></see>", " />" }
34-
};
35-
36-
private static readonly Dictionary<string, string> _replaceableMarkdownPatterns = new Dictionary<string, string> {
37-
{ "<see langword=\"null\"/>", "`null`" },
38-
{ "<see langword=\"null\" />", "`null`" },
39-
{ "<see langword=\"true\"/>", "`true`" },
40-
{ "<see langword=\"true\" />", "`true`" },
41-
{ "<see langword=\"false\"/>", "`false`" },
42-
{ "<see langword=\"false\" />", "`false`" },
43-
{ "<see cref=\"T:", "<xref:" },
44-
{ "<see cref=\"F:", "<xref:" },
45-
{ "<see cref=\"M:", "<xref:" },
46-
{ "<see cref=\"P:", "<xref:" },
47-
{ "<see cref=\"", "<xref:" },
48-
{ " null ", " `null` " },
49-
{ "'null'", "`null`" },
50-
{ " null.", " `null`." },
51-
{ " null,", " `null`," },
52-
{ " false ", " `false` " },
53-
{ "'false'", "`false`" },
54-
{ " false.", " `false`." },
55-
{ " false,", " `false`," },
56-
{ " true ", " `true` " },
57-
{ "'true'", "`true`" },
58-
{ " true.", " `true`." },
59-
{ " true,", " `true`," },
60-
{ "null ", "`null` " },
61-
{ "true ", "`true` " },
62-
{ "false ", "`false` " },
63-
{ "Null ", "`null` " },
64-
{ "True ", "`true` " },
65-
{ "False ", "`false` " },
66-
{ "<c>", "`"},
67-
{ "</c>", "`"},
68-
{ "<para>", "" },
69-
{ "</para>", "\r\n\r\n" },
70-
{ "\" />", ">" },
71-
{ "<![CDATA[", "" },
72-
{ "]]>", "" },
73-
{ "<note type=\"inheritinfo\">", ""},
74-
{ "</note>", "" }
75-
};
76-
77-
private static readonly Dictionary<string, string> _replaceableExceptionPatterns = new Dictionary<string, string>{
78-
79-
{ "<para>", "\r\n" },
80-
{ "</para>", "" }
81-
};
82-
83-
private static readonly Dictionary<string, string> _replaceableMarkdownRegexPatterns = new Dictionary<string, string> {
84-
{ @"\<paramref name\=""(?'paramrefContents'[a-zA-Z0-9_\-]+)""[ ]*\/\>", @"`${paramrefContents}`" },
85-
{ @"\<seealso cref\=""(?'seealsoContents'.+)""[ ]*\/\>", @"seealsoContents" },
13+
private static readonly (string, string)[] ReplaceableMarkdownPatterns = new[]
14+
{
15+
(@"\s*<!\[CDATA\[\s*", ""),
16+
(@"\s*\]\]>\s*", ""),
17+
(@"\s*##\s*Remarks\s*", ""),
18+
(@"`(?'keyword'null|false|true)`", "<see langword=\"${keyword}\" />"),
19+
(@"<c>(?'keyword'null|false|true)</c>", "<see langword=\"${keyword}\" />"),
20+
(@"<xref:(?'docId'[a-zA-Z0-9\._\@\#\$\%\(\)\[\]<>\?\,]+)>", "<see cref=\"${docId}\" />"),
8621
};
8722

8823
public static string GetAttributeValue(XElement parent, string name)
@@ -120,199 +55,50 @@ public static string GetChildElementValue(XElement parent, string childName)
12055

12156
if (child != null)
12257
{
123-
return GetNodesInPlainText(child);
58+
return GetNodesInPlainText(childName, child);
12459
}
12560

12661
return string.Empty;
12762
}
12863

129-
public static string GetNodesInPlainText(XElement element)
64+
public static string GetNodesInPlainText(string name, XElement element)
13065
{
13166
if (element == null)
13267
{
13368
throw new Exception("A null element was passed when attempting to retrieve the nodes in plain text.");
13469
}
13570

71+
if (name == "remarks")
72+
{
73+
XElement? formatElement = element.Element("format");
74+
if (formatElement != null)
75+
{
76+
element = formatElement;
77+
}
78+
}
13679
// string.Join("", element.Nodes()) is very slow.
13780
//
13881
// The following is twice as fast (although still slow)
13982
// but does not produce the same spacing. That may be OK.
14083
//
141-
//using var reader = element.CreateReader();
142-
//reader.MoveToContent();
143-
//return reader.ReadInnerXml().Trim();
144-
145-
string actualValue = string.Join("", element.Nodes()).Trim();
146-
return actualValue.IsDocsEmpty() ? string.Empty : actualValue;
147-
}
148-
149-
public static void SaveFormattedAsMarkdown(XElement element, string newValue, bool isMember)
150-
{
151-
if (element == null)
152-
{
153-
throw new Exception("A null element was passed when attempting to save formatted as markdown");
154-
}
155-
156-
// Empty value because SaveChildElement will add a child to the parent, not replace it
157-
element.Value = string.Empty;
158-
159-
XElement xeFormat = new XElement("format");
160-
161-
string updatedValue = SubstituteRemarksRegexPatterns(newValue);
162-
updatedValue = ReplaceMarkdownPatterns(updatedValue).Trim();
163-
164-
string remarksTitle = string.Empty;
165-
if (!updatedValue.Contains("## Remarks"))
166-
{
167-
remarksTitle = "## Remarks\r\n\r\n";
168-
}
169-
170-
string spaces = isMember ? " " : " ";
171-
172-
xeFormat.ReplaceAll(new XCData("\r\n\r\n" + remarksTitle + updatedValue + "\r\n\r\n" + spaces));
173-
174-
// Attribute at the end, otherwise it would be replaced by ReplaceAll
175-
xeFormat.SetAttributeValue("type", "text/markdown");
176-
177-
element.Add(xeFormat);
178-
}
179-
180-
public static void AddChildFormattedAsMarkdown(XElement parent, XElement child, string childValue, bool isMember)
181-
{
182-
if (parent == null)
183-
{
184-
throw new Exception("A null parent was passed when attempting to add child formatted as markdown.");
185-
}
186-
187-
if (child == null)
188-
{
189-
throw new Exception("A null child was passed when attempting to add child formatted as markdown.");
190-
}
191-
192-
SaveFormattedAsMarkdown(child, childValue, isMember);
193-
parent.Add(child);
194-
}
195-
196-
public static void SaveFormattedAsXml(XElement element, string newValue, bool removeUndesiredEndlines = true)
197-
{
198-
if (element == null)
199-
{
200-
throw new Exception("A null element was passed when attempting to save formatted as xml");
201-
}
202-
203-
element.Value = string.Empty;
204-
205-
var attributes = element.Attributes();
206-
207-
string updatedValue = removeUndesiredEndlines ? RemoveUndesiredEndlines(newValue) : newValue;
208-
updatedValue = ReplaceNormalElementPatterns(updatedValue);
209-
210-
// Workaround: <x> will ensure XElement does not complain about having an invalid xml object inside. Those tags will be removed by replacing the nodes.
211-
XElement parsedElement;
212-
try
213-
{
214-
parsedElement = XElement.Parse("<x>" + updatedValue + "</x>");
215-
}
216-
catch (XmlException)
217-
{
218-
parsedElement = XElement.Parse("<x>" + updatedValue.Replace("<", "&lt;").Replace(">", "&gt;") + "</x>");
219-
}
220-
221-
element.ReplaceNodes(parsedElement.Nodes());
222-
223-
// Ensure attributes are preserved after replacing nodes
224-
element.ReplaceAttributes(attributes);
225-
}
226-
227-
public static void AppendFormattedAsXml(XElement element, string valueToAppend, bool removeUndesiredEndlines)
228-
{
229-
if (element == null)
230-
{
231-
throw new Exception("A null element was passed when attempting to append formatted as xml");
232-
}
233-
234-
SaveFormattedAsXml(element, GetNodesInPlainText(element) + valueToAppend, removeUndesiredEndlines);
235-
}
84+
using XmlReader reader = element.CreateReader();
85+
reader.MoveToContent();
86+
string actualValue = reader.ReadInnerXml().Trim();
23687

237-
public static void AddChildFormattedAsXml(XElement parent, XElement child, string childValue)
238-
{
239-
if (parent == null)
88+
if (name == "remarks")
24089
{
241-
throw new Exception("A null parent was passed when attempting to add child formatted as xml");
90+
actualValue = ReplaceMarkdown(actualValue);
24291
}
24392

244-
if (child == null)
245-
{
246-
throw new Exception("A null child was passed when attempting to add child formatted as xml");
247-
}
248-
249-
SaveFormattedAsXml(child, childValue);
250-
parent.Add(child);
251-
}
252-
253-
private static string RemoveUndesiredEndlines(string value)
254-
{
255-
value = Regex.Replace(value, @"((?'undesiredEndlinePrefix'[^\.\:])(\r\n)+[ \t]*)", @"${undesiredEndlinePrefix} ");
256-
257-
return value.Trim();
258-
}
259-
260-
private static string SubstituteRemarksRegexPatterns(string value)
261-
{
262-
return SubstituteRegexPatterns(value, _replaceableMarkdownRegexPatterns);
263-
}
264-
265-
private static string ReplaceMarkdownPatterns(string value)
266-
{
267-
string updatedValue = value;
268-
foreach (KeyValuePair<string, string> kvp in _replaceableMarkdownPatterns)
269-
{
270-
if (updatedValue.Contains(kvp.Key))
271-
{
272-
updatedValue = updatedValue.Replace(kvp.Key, kvp.Value);
273-
}
274-
}
275-
return updatedValue;
276-
}
277-
278-
internal static string ReplaceExceptionPatterns(string value)
279-
{
280-
string updatedValue = value;
281-
foreach (KeyValuePair<string, string> kvp in _replaceableExceptionPatterns)
282-
{
283-
if (updatedValue.Contains(kvp.Key))
284-
{
285-
updatedValue = updatedValue.Replace(kvp.Key, kvp.Value);
286-
}
287-
}
288-
289-
updatedValue = Regex.Replace(updatedValue, @"[\r\n\t ]+\-[ ]?or[ ]?\-[\r\n\t ]+", "\r\n\r\n-or-\r\n\r\n");
290-
return updatedValue;
291-
}
292-
293-
private static string ReplaceNormalElementPatterns(string value)
294-
{
295-
string updatedValue = value;
296-
foreach (KeyValuePair<string, string> kvp in _replaceableNormalElementPatterns)
297-
{
298-
if (updatedValue.Contains(kvp.Key))
299-
{
300-
updatedValue = updatedValue.Replace(kvp.Key, kvp.Value);
301-
}
302-
}
303-
304-
return updatedValue;
93+
//string actualValue = string.Join("", element.Nodes()).Trim();
94+
return actualValue.IsDocsEmpty() ? string.Empty : actualValue;
30595
}
30696

307-
private static string SubstituteRegexPatterns(string value, Dictionary<string, string> replaceableRegexPatterns)
97+
private static string ReplaceMarkdown(string value)
30898
{
309-
foreach (KeyValuePair<string, string> pattern in replaceableRegexPatterns)
99+
foreach ((string bad, string good) in ReplaceableMarkdownPatterns)
310100
{
311-
Regex regex = new Regex(pattern.Key);
312-
if (regex.IsMatch(value))
313-
{
314-
value = regex.Replace(value, pattern.Value);
315-
}
101+
value = Regex.Replace(value, bad, good);
316102
}
317103

318104
return value;

0 commit comments

Comments
 (0)