|
3 | 3 | using System.Linq; |
4 | 4 | using System.Xml; |
5 | 5 | using System.Xml.XPath; |
| 6 | +using System.Xml.Linq; |
6 | 7 |
|
7 | 8 | using Xamarin.Android.Tools; |
8 | 9 |
|
9 | 10 | namespace MonoDroid.Generation |
10 | 11 | { |
11 | 12 | public class ApiFixup |
12 | 13 | { |
13 | | - XmlDocument api_doc; |
| 14 | + XDocument api_doc; |
14 | 15 | string apiSource = ""; |
15 | 16 |
|
16 | 17 | public string ApiSource { get { return apiSource; } } |
17 | 18 |
|
18 | | - public ApiFixup (XmlDocument apiDoc) |
| 19 | + public ApiFixup (XDocument apiDoc) |
19 | 20 | { |
20 | 21 | api_doc = apiDoc; |
21 | | - var api = api_doc.DocumentElement; |
| 22 | + var api = api_doc.Root; |
22 | 23 | if (api != null) |
23 | 24 | apiSource = api.XGetAttribute ("api-source"); |
24 | 25 | } |
25 | 26 |
|
26 | | - public void Process (IEnumerable<XmlDocument> metaDocs, string apiLevel, int productVersion) |
| 27 | + public void Process (IEnumerable<XDocument> metaDocs, string apiLevel, int productVersion) |
27 | 28 | { |
28 | 29 | foreach (var metaDoc in metaDocs) |
29 | 30 | Process (metaDoc, apiLevel, productVersion); |
30 | 31 | } |
31 | 32 |
|
32 | | - bool ShouldSkip (XPathNavigator node, int apiLevel, int productVersion) |
| 33 | + bool ShouldSkip (XElement node, int apiLevel, int productVersion) |
33 | 34 | { |
34 | 35 | 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"); |
37 | 38 | if (!string.IsNullOrEmpty (apiSince) && int.Parse (apiSince) > apiLevel) |
38 | 39 | return true; |
39 | 40 | if (!string.IsNullOrEmpty (apiUntil) && int.Parse (apiUntil) < apiLevel) |
40 | 41 | return true; |
41 | 42 | } |
42 | 43 | if (productVersion > 0) { |
43 | | - var product_version = node.XGetAttribute ("product-version", ""); |
| 44 | + var product_version = node.XGetAttribute ("product-version"); |
44 | 45 | if (!string.IsNullOrEmpty (product_version) && int.Parse (product_version) > productVersion) |
45 | 46 | return true; |
46 | 47 | } |
47 | 48 | return false; |
48 | 49 | } |
49 | 50 |
|
50 | | - bool ShouldApply (XPathNavigator node) |
| 51 | + bool ShouldApply (XElement node) |
51 | 52 | { |
52 | 53 | if (!string.IsNullOrEmpty (apiSource)) { |
53 | | - var targetsource = node.XGetAttribute ("api-source", ""); |
| 54 | + var targetsource = node.XGetAttribute ("api-source"); |
54 | 55 | if (string.IsNullOrEmpty (targetsource)) |
55 | 56 | return true; |
56 | 57 | return targetsource == apiSource; |
57 | 58 | } |
58 | 59 | return true; |
59 | 60 | } |
60 | 61 |
|
61 | | - void Process (XmlDocument meta_doc, string apiLevelString, int productVersion) |
| 62 | + void Process (XDocument meta_doc, string apiLevelString, int productVersion) |
62 | 63 | { |
63 | | - XPathNavigator api_nav = api_doc.CreateNavigator (); |
64 | | - XPathNavigator meta_nav = meta_doc.CreateNavigator (); |
65 | 64 | int apiLevel = 0; |
66 | 65 | int.TryParse (apiLevelString, out apiLevel); |
67 | 66 |
|
68 | | - XPathNodeIterator metadata = meta_nav.Select ("/metadata/*"); |
| 67 | + var metadataChildren = meta_doc.XPathSelectElements ("/metadata/*"); |
69 | 68 | string prev_path = null; |
70 | | - XPathNavigator attr_last_cache = null; |
| 69 | + XElement attr_last_cache = null; |
71 | 70 |
|
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)) |
75 | 73 | continue; |
76 | | - if (!ShouldApply (metanav)) |
| 74 | + if (!ShouldApply (metaitem)) |
77 | 75 | continue; |
78 | | - string path = metanav.XGetAttribute ("path", ""); |
| 76 | + string path = metaitem.XGetAttribute ("path"); |
79 | 77 | if (path != prev_path) |
80 | 78 | attr_last_cache = null; |
81 | 79 | prev_path = path; |
82 | 80 |
|
83 | | - switch (metanav.LocalName) { |
| 81 | + switch (metaitem.Name.LocalName) { |
84 | 82 | case "remove-node": |
85 | 83 | 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).ToArray (); |
| 85 | + if (nodes.Any ()) |
| 86 | + foreach (var node in nodes) |
| 87 | + node.Remove (); |
| 88 | + else |
93 | 89 | // BG8A00 |
94 | | - Report.Warning (0, Report.WarningApiFixup + 0, "<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); |
95 | 91 | } catch (XPathException e) { |
96 | 92 | // BG4A01 |
97 | | - Report.Error (Report.ErrorApiFixup + 1, e, "Invalid XPath specification: {0}", path); |
| 93 | + Report.Error (Report.ErrorApiFixup + 1, e, metaitem, "Invalid XPath specification: {0}", path); |
98 | 94 | } |
99 | 95 | break; |
100 | 96 | case "add-node": |
101 | 97 | try { |
102 | | - XPathNodeIterator api_iter = api_nav.Select (path); |
| 98 | + var nodes = api_doc.XPathSelectElements (path); |
103 | 99 | 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 ()); |
108 | 106 | matched = true; |
109 | 107 | } |
110 | | - if (!matched) |
111 | | - // BG8A01 |
112 | | - Report.Warning (0, Report.WarningApiFixup + 1, "<add-node path=\"{0}\"/> matched no nodes.", path); |
113 | 108 | } catch (XPathException e) { |
114 | 109 | // BG4A02 |
115 | | - Report.Error (Report.ErrorApiFixup + 2, e, "Invalid XPath specification: {0}", path); |
| 110 | + Report.Error (Report.ErrorApiFixup + 2, e, metaitem, "Invalid XPath specification: {0}", path); |
116 | 111 | } |
117 | 112 | break; |
118 | 113 | case "change-node": |
119 | 114 | try { |
120 | | - XPathNodeIterator api_iter = api_nav.Select (path); |
| 115 | + var nodes = api_doc.XPathSelectElements (path); |
121 | 116 | 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); |
133 | 122 | matched = true; |
134 | 123 | } |
135 | 124 |
|
136 | 125 | if (!matched) |
137 | 126 | // BG8A03 |
138 | | - Report.Warning (0, Report.WarningApiFixup + 3, "<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); |
139 | 128 | } catch (XPathException e) { |
140 | 129 | // BG4A03 |
141 | | - Report.Error (Report.ErrorApiFixup + 3, e, "Invalid XPath specification: {0}", path); |
| 130 | + Report.Error (Report.ErrorApiFixup + 3, e, metaitem, "Invalid XPath specification: {0}", path); |
142 | 131 | } |
143 | 132 | break; |
144 | 133 | case "attr": |
145 | 134 | try { |
146 | | - string attr_name = metanav.XGetAttribute ("name", ""); |
| 135 | + string attr_name = metaitem.XGetAttribute ("name"); |
147 | 136 | if (string.IsNullOrEmpty (attr_name)) |
148 | 137 | // BG4A07 |
149 | | - Report.Error (Report.ErrorApiFixup + 7, "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); |
153 | 140 | int attr_matched = 0; |
154 | 141 | 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); |
158 | 143 | attr_matched++; |
159 | 144 | } |
160 | 145 | if (attr_matched == 0) |
161 | 146 | // BG8A04 |
162 | | - Report.Warning (0, Report.WarningApiFixup + 4, "<attr path=\"{0}\"/> matched no nodes.", path); |
| 147 | + Report.Warning (0, Report.WarningApiFixup + 4, null, metaitem, "<attr path=\"{0}\"/> matched no nodes.", path); |
163 | 148 | if (attr_matched != 1) |
164 | 149 | attr_last_cache = null; |
165 | 150 | } catch (XPathException e) { |
166 | 151 | // BG4A04 |
167 | | - Report.Error (Report.ErrorApiFixup + 4, e, "Invalid XPath specification: {0}", path); |
| 152 | + Report.Error (Report.ErrorApiFixup + 4, e, metaitem, "Invalid XPath specification: {0}", path); |
168 | 153 | } |
169 | 154 | break; |
170 | 155 | case "move-node": |
171 | 156 | 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); |
175 | 159 | 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).ToArray (); |
| 162 | + foreach (var node in nodes) |
| 163 | + node.Remove (); |
| 164 | + parent_node.Add (nodes); |
184 | 165 | matched = true; |
185 | 166 | } |
186 | 167 | if (!matched) |
187 | 168 | // BG8A05 |
188 | | - Report.Warning (0, Report.WarningApiFixup + 5, "<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); |
189 | 170 | } catch (XPathException e) { |
190 | 171 | // BG4A05 |
191 | | - Report.Error (Report.ErrorApiFixup + 5, e, "Invalid XPath specification: {0}", path); |
| 172 | + Report.Error (Report.ErrorApiFixup + 5, e, metaitem, "Invalid XPath specification: {0}", path); |
192 | 173 | } |
193 | 174 | break; |
194 | 175 | case "remove-attr": |
195 | 176 | 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); |
198 | 179 | 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 (); |
204 | 183 | matched = true; |
205 | 184 | } |
206 | 185 |
|
207 | 186 | if (!matched) |
208 | 187 | // BG8A06 |
209 | | - Report.Warning (0, Report.WarningApiFixup + 6, "<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); |
210 | 189 | } catch (XPathException e) { |
211 | 190 | // BG4A06 |
212 | | - Report.Error (Report.ErrorApiFixup + 6, e, "Invalid XPath specification: {0}", path); |
| 191 | + Report.Error (Report.ErrorApiFixup + 6, e, metaitem, "Invalid XPath specification: {0}", path); |
213 | 192 | } |
214 | 193 | break; |
215 | 194 | } |
|
0 commit comments