Skip to content

Commit cc31866

Browse files
committed
[generator] Replace XmlDocument based DOM with Linq to XML.
1 parent fe9009a commit cc31866

15 files changed

+201
-227
lines changed

src/utils/EnumMappings.Xml.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
using System.IO;
33
using System.Linq;
44
using System.Xml;
5-
5+
using System.Xml.Linq;
6+
using System.Xml.XPath;
67
using Xamarin.Android.Tools;
78

89
namespace MonoDroid.Generation {
@@ -23,19 +24,18 @@ internal static TextReader FieldXmlToCsv (string file)
2324
return null;
2425

2526
var sw = new StringWriter ();
26-
var doc = new XmlDocument ();
27-
doc.Load (file);
27+
var doc = XDocument.Load (file, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
2828

29-
foreach (XmlElement e in doc.SelectNodes ("/enum-field-mappings/mapping")) {
29+
foreach (var e in doc.XPathSelectElements ("/enum-field-mappings/mapping")) {
3030
string enu = GetMandatoryAttribute (e, "clr-enum-type");
31-
string jni_type = e.HasAttribute ("jni-class")
31+
string jni_type = e.Attribute ("jni-class") != null
3232
? e.XGetAttribute ("jni-class")
33-
: e.HasAttribute ("jni-interface")
33+
: e.Attribute ("jni-interface") != null
3434
? "I:" + e.XGetAttribute ("jni-interface")
3535
: GetMandatoryAttribute (e, "jni-class or jni-interface");
36-
bool bitfield = e.HasAttribute ("bitfield") && e.XGetAttribute ("bitfield") == "true";
37-
foreach (XmlElement m in e.SelectNodes ("field")) {
38-
string verstr = m.HasAttribute ("api-level")
36+
bool bitfield = e.Attribute ("bitfield") != null && e.XGetAttribute ("bitfield") == "true";
37+
foreach (var m in e.XPathSelectElements ("field")) {
38+
string verstr = m.Attribute ("api-level") != null
3939
? m.XGetAttribute ("api-level")
4040
: "0";
4141
string member = GetMandatoryAttribute (m, "clr-name");
@@ -48,10 +48,10 @@ internal static TextReader FieldXmlToCsv (string file)
4848
return new StringReader (sw.ToString ());
4949
}
5050

51-
static string GetMandatoryAttribute (XmlElement e, string name)
51+
static string GetMandatoryAttribute (XElement e, string name)
5252
{
53-
if (!e.HasAttribute (name)) {
54-
throw new InvalidOperationException (String.Format ("Mandatory attribute '{0}' is missing on a mapping element: {1}", name, e.OuterXml));
53+
if (e.Attribute (name) != null) {
54+
throw new InvalidOperationException (String.Format ("Mandatory attribute '{0}' is missing on a mapping element: {1}", name, e.ToString ()));
5555
}
5656
return e.XGetAttribute (name);
5757
}

src/utils/XmlExtensions.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,34 @@
22
using System.IO;
33
using System.Linq;
44
using System.Xml;
5+
using System.Xml.Linq;
56
using System.Xml.XPath;
67

78
namespace Xamarin.Android.Tools {
89

910
static class XmlExtensions {
1011

11-
public static string XGetAttribute (this XmlElement element, string name)
12+
public static string XGetAttribute (this XElement element, string name)
1213
{
13-
var attr = element.GetAttribute (name);
14-
return attr != null ? attr.Trim () : null;
14+
var attr = element.Attribute (name);
15+
return attr != null ? attr.Value.Trim () : null;
1516
}
1617

1718
public static string XGetAttribute (this XPathNavigator nav, string name, string ns)
1819
{
1920
var attr = nav.GetAttribute (name, ns);
2021
return attr != null ? attr.Trim () : null;
2122
}
23+
24+
public static void XSetAttribute (this XPathNavigator nav, string name, string ns, string value)
25+
{
26+
Console.Error.WriteLine (nav.GetType ());
27+
if (nav.MoveToAttribute (name, ns)) {
28+
nav.SetValue (value);
29+
nav.MoveToParent ();
30+
} else
31+
// phew, it is super hacky code to workaround the issue that XNodeNavigator (XLinq XPathNodeNavigator) does not support CreateAttributes()!
32+
nav.OuterXml = nav.OuterXml.Substring (0, nav.Name.Length + 1).Replace ("<" + nav.Name, $"<{nav.Name} {name}=\'{value}'") + nav.OuterXml.Substring (nav.Name.Length + 1);
33+
}
2234
}
2335
}

tools/generator/ApiFixup.cs

Lines changed: 64 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,213 +3,192 @@
33
using System.Linq;
44
using System.Xml;
55
using System.Xml.XPath;
6+
using System.Xml.Linq;
67

78
using Xamarin.Android.Tools;
89

910
namespace MonoDroid.Generation
1011
{
1112
public class ApiFixup
1213
{
13-
XmlDocument api_doc;
14+
XDocument api_doc;
1415
string apiSource = "";
1516

1617
public string ApiSource { get { return apiSource; } }
1718

18-
public ApiFixup (XmlDocument apiDoc)
19+
public ApiFixup (XDocument apiDoc)
1920
{
2021
api_doc = apiDoc;
21-
var api = api_doc.DocumentElement;
22+
var api = api_doc.Root;
2223
if (api != null)
2324
apiSource = api.XGetAttribute ("api-source");
2425
}
2526

26-
public void Process (IEnumerable<XmlDocument> metaDocs, string apiLevel, int productVersion)
27+
public void Process (IEnumerable<XDocument> metaDocs, string apiLevel, int productVersion)
2728
{
2829
foreach (var metaDoc in metaDocs)
2930
Process (metaDoc, apiLevel, productVersion);
3031
}
3132

32-
bool ShouldSkip (XPathNavigator node, int apiLevel, int productVersion)
33+
bool ShouldSkip (XElement node, int apiLevel, int productVersion)
3334
{
3435
if (apiLevel > 0) {
35-
string apiSince = node.XGetAttribute ("api-since", "");
36-
string apiUntil = node.XGetAttribute ("api-until", "");
36+
string apiSince = node.XGetAttribute ("api-since");
37+
string apiUntil = node.XGetAttribute ("api-until");
3738
if (!string.IsNullOrEmpty (apiSince) && int.Parse (apiSince) > apiLevel)
3839
return true;
3940
if (!string.IsNullOrEmpty (apiUntil) && int.Parse (apiUntil) < apiLevel)
4041
return true;
4142
}
4243
if (productVersion > 0) {
43-
var product_version = node.XGetAttribute ("product-version", "");
44+
var product_version = node.XGetAttribute ("product-version");
4445
if (!string.IsNullOrEmpty (product_version) && int.Parse (product_version) > productVersion)
4546
return true;
4647
}
4748
return false;
4849
}
4950

50-
bool ShouldApply (XPathNavigator node)
51+
bool ShouldApply (XElement node)
5152
{
5253
if (!string.IsNullOrEmpty (apiSource)) {
53-
var targetsource = node.XGetAttribute ("api-source", "");
54+
var targetsource = node.XGetAttribute ("api-source");
5455
if (string.IsNullOrEmpty (targetsource))
5556
return true;
5657
return targetsource == apiSource;
5758
}
5859
return true;
5960
}
6061

61-
void Process (XmlDocument meta_doc, string apiLevelString, int productVersion)
62+
void Process (XDocument meta_doc, string apiLevelString, int productVersion)
6263
{
63-
XPathNavigator api_nav = api_doc.CreateNavigator ();
64-
XPathNavigator meta_nav = meta_doc.CreateNavigator ();
6564
int apiLevel = 0;
6665
int.TryParse (apiLevelString, out apiLevel);
6766

68-
XPathNodeIterator metadata = meta_nav.Select ("/metadata/*");
67+
var metadataChildren = meta_doc.XPathSelectElements ("/metadata/*");
6968
string prev_path = null;
70-
XPathNavigator attr_last_cache = null;
69+
XElement attr_last_cache = null;
7170

72-
while (metadata.MoveNext ()) {
73-
var metanav = metadata.Current;
74-
if (ShouldSkip (metanav, apiLevel, productVersion))
71+
foreach (var metaitem in metadataChildren) {
72+
if (ShouldSkip (metaitem, apiLevel, productVersion))
7573
continue;
76-
if (!ShouldApply (metanav))
74+
if (!ShouldApply (metaitem))
7775
continue;
78-
string path = metanav.XGetAttribute ("path", "");
76+
string path = metaitem.XGetAttribute ("path");
7977
if (path != prev_path)
8078
attr_last_cache = null;
8179
prev_path = path;
8280

83-
switch (metanav.LocalName) {
81+
switch (metaitem.Name.LocalName) {
8482
case "remove-node":
8583
try {
86-
XPathNodeIterator api_iter = api_nav.Select (path);
87-
List<XmlElement> matches = new List<XmlElement> ();
88-
while (api_iter.MoveNext ())
89-
matches.Add (((IHasXmlNode)api_iter.Current).GetNode () as XmlElement);
90-
foreach (XmlElement api_node in matches)
91-
api_node.ParentNode.RemoveChild (api_node);
92-
if (matches.Count == 0)
84+
var nodes = api_doc.XPathSelectElements (path);
85+
if (nodes.Any ())
86+
foreach (var node in nodes)
87+
node.Remove ();
88+
else
9389
// BG8A00
94-
Report.Warning (0, Report.WarningApiFixup + 0, null, metanav, "<remove-node path=\"{0}\"/> matched no nodes.", path);
90+
Report.Warning (0, Report.WarningApiFixup + 0, null, metaitem, "<remove-node path=\"{0}\"/> matched no nodes.", path);
9591
} catch (XPathException e) {
9692
// BG4A01
97-
Report.Error (Report.ErrorApiFixup + 1, e, metanav, "Invalid XPath specification: {0}", path);
93+
Report.Error (Report.ErrorApiFixup + 1, e, metaitem, "Invalid XPath specification: {0}", path);
9894
}
9995
break;
10096
case "add-node":
10197
try {
102-
XPathNodeIterator api_iter = api_nav.Select (path);
98+
var nodes = api_doc.XPathSelectElements (path);
10399
bool matched = false;
104-
while (api_iter.MoveNext ()) {
105-
XmlElement api_node = ((IHasXmlNode)api_iter.Current).GetNode () as XmlElement;
106-
foreach (XmlNode child in ((IHasXmlNode)metanav).GetNode().ChildNodes)
107-
api_node.AppendChild (api_doc.ImportNode (child, true));
100+
if (!nodes.Any ())
101+
// BG8A01
102+
Report.Warning (0, Report.WarningApiFixup + 1, null, metaitem, "<add-node path=\"{0}\"/> matched no nodes.", path);
103+
else {
104+
foreach (var node in nodes)
105+
node.Add (metaitem.Nodes ());
108106
matched = true;
109107
}
110-
if (!matched)
111-
// BG8A01
112-
Report.Warning (0, Report.WarningApiFixup + 1, null, metanav, "<add-node path=\"{0}\"/> matched no nodes.", path);
113108
} catch (XPathException e) {
114109
// BG4A02
115-
Report.Error (Report.ErrorApiFixup + 2, e, metanav, "Invalid XPath specification: {0}", path);
110+
Report.Error (Report.ErrorApiFixup + 2, e, metaitem, "Invalid XPath specification: {0}", path);
116111
}
117112
break;
118113
case "change-node":
119114
try {
120-
XPathNodeIterator api_iter = api_nav.Select (path);
115+
var nodes = api_doc.XPathSelectElements (path);
121116
bool matched = false;
122-
while (api_iter.MoveNext ()) {
123-
XmlElement node = ( (IHasXmlNode) api_iter.Current).GetNode () as XmlElement;
124-
XmlElement parent = node.ParentNode as XmlElement;
125-
XmlElement new_node = api_doc.CreateElement (metanav.Value);
126-
127-
foreach (XmlNode child in node.ChildNodes)
128-
new_node.AppendChild (child.Clone ());
129-
foreach (XmlAttribute attribute in node.Attributes)
130-
new_node.Attributes.Append ( (XmlAttribute) attribute.Clone ());
131-
132-
parent.ReplaceChild (new_node, node);
117+
foreach (var node in nodes) {
118+
var newChild = new XElement (metaitem.Value);
119+
newChild.Add (node.Attributes ());
120+
newChild.Add (node.Nodes ());
121+
node.ReplaceWith (newChild);
133122
matched = true;
134123
}
135124

136125
if (!matched)
137126
// BG8A03
138-
Report.Warning (0, Report.WarningApiFixup + 3, null, metanav, "<change-node-type path=\"{0}\"/> matched no nodes.", path);
127+
Report.Warning (0, Report.WarningApiFixup + 3, null, metaitem, "<change-node-type path=\"{0}\"/> matched no nodes.", path);
139128
} catch (XPathException e) {
140129
// BG4A03
141-
Report.Error (Report.ErrorApiFixup + 3, e, metanav, "Invalid XPath specification: {0}", path);
130+
Report.Error (Report.ErrorApiFixup + 3, e, metaitem, "Invalid XPath specification: {0}", path);
142131
}
143132
break;
144133
case "attr":
145134
try {
146-
string attr_name = metanav.XGetAttribute ("name", "");
135+
string attr_name = metaitem.XGetAttribute ("name");
147136
if (string.IsNullOrEmpty (attr_name))
148137
// BG4A07
149-
Report.Error (Report.ErrorApiFixup + 7, null, metanav, "Target attribute name is not specified for path: {0}", path);
150-
var nodes = attr_last_cache != null ?
151-
(IEnumerable<XPathNavigator>) new XPathNavigator [] {attr_last_cache} :
152-
api_nav.Select (path).OfType<XPathNavigator> ();
138+
Report.Error (Report.ErrorApiFixup + 7, null, metaitem, "Target attribute name is not specified for path: {0}", path);
139+
var nodes = attr_last_cache != null ? new XElement [] { attr_last_cache } : api_doc.XPathSelectElements (path);
153140
int attr_matched = 0;
154141
foreach (var n in nodes) {
155-
XmlElement node = ((IHasXmlNode) n).GetNode () as XmlElement;
156-
node.SetAttribute (attr_name, metanav.Value);
157-
//attr_last_cache = n;
142+
n.SetAttributeValue (attr_name, metaitem.Value);
158143
attr_matched++;
159144
}
160145
if (attr_matched == 0)
161146
// BG8A04
162-
Report.Warning (0, Report.WarningApiFixup + 4, null, metanav, "<attr path=\"{0}\"/> matched no nodes.", path);
147+
Report.Warning (0, Report.WarningApiFixup + 4, null, metaitem, "<attr path=\"{0}\"/> matched no nodes.", path);
163148
if (attr_matched != 1)
164149
attr_last_cache = null;
165150
} catch (XPathException e) {
166151
// BG4A04
167-
Report.Error (Report.ErrorApiFixup + 4, e, metanav, "Invalid XPath specification: {0}", path);
152+
Report.Error (Report.ErrorApiFixup + 4, e, metaitem, "Invalid XPath specification: {0}", path);
168153
}
169154
break;
170155
case "move-node":
171156
try {
172-
XPathExpression expr = api_nav.Compile (path);
173-
string parent = metanav.Value;
174-
XPathNodeIterator parent_iter = api_nav.Select (parent);
157+
string parent = metaitem.Value;
158+
var parents = api_doc.XPathSelectElements (parent);
175159
bool matched = false;
176-
while (parent_iter.MoveNext ()) {
177-
XmlNode parent_node = ((IHasXmlNode)parent_iter.Current).GetNode ();
178-
XPathNodeIterator path_iter = parent_iter.Current.Clone ().Select (expr);
179-
while (path_iter.MoveNext ()) {
180-
XmlNode node = ((IHasXmlNode)path_iter.Current).GetNode ();
181-
parent_node.AppendChild (node.Clone ());
182-
node.ParentNode.RemoveChild (node);
183-
}
160+
foreach (var parent_node in parents) {
161+
var nodes = parent_node.XPathSelectElements (path);
162+
foreach (var node in nodes)
163+
node.Remove ();
164+
parent_node.Add (nodes);
184165
matched = true;
185166
}
186167
if (!matched)
187168
// BG8A05
188-
Report.Warning (0, Report.WarningApiFixup + 5, null, metanav, "<move-node path=\"{0}\"/> matched no nodes.", path);
169+
Report.Warning (0, Report.WarningApiFixup + 5, null, metaitem, "<move-node path=\"{0}\"/> matched no nodes.", path);
189170
} catch (XPathException e) {
190171
// BG4A05
191-
Report.Error (Report.ErrorApiFixup + 5, e, metanav, "Invalid XPath specification: {0}", path);
172+
Report.Error (Report.ErrorApiFixup + 5, e, metaitem, "Invalid XPath specification: {0}", path);
192173
}
193174
break;
194175
case "remove-attr":
195176
try {
196-
string name = metanav.XGetAttribute ("name", "");
197-
XPathNodeIterator api_iter = api_nav.Select (path);
177+
string name = metaitem.XGetAttribute ("name");
178+
var nodes = api_doc.XPathSelectElements (path);
198179
bool matched = false;
199-
200-
while (api_iter.MoveNext ()) {
201-
XmlElement node = ( (IHasXmlNode) api_iter.Current).GetNode () as XmlElement;
202-
203-
node.RemoveAttribute (name);
180+
181+
foreach (var node in nodes) {
182+
node.RemoveAttributes ();
204183
matched = true;
205184
}
206185

207186
if (!matched)
208187
// BG8A06
209-
Report.Warning (0, Report.WarningApiFixup + 6, null, metanav, "<remove-attr path=\"{0}\"/> matched no nodes.", path);
188+
Report.Warning (0, Report.WarningApiFixup + 6, null, metaitem, "<remove-attr path=\"{0}\"/> matched no nodes.", path);
210189
} catch (XPathException e) {
211190
// BG4A06
212-
Report.Error (Report.ErrorApiFixup + 6, e, metanav, "Invalid XPath specification: {0}", path);
191+
Report.Error (Report.ErrorApiFixup + 6, e, metaitem, "Invalid XPath specification: {0}", path);
213192
}
214193
break;
215194
}

0 commit comments

Comments
 (0)