|
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