Skip to content

Commit 39998f3

Browse files
committed
[generator] Separate metadata fixup step from parsing step.
1 parent a3de91e commit 39998f3

File tree

26 files changed

+395
-116
lines changed

26 files changed

+395
-116
lines changed

src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Diagnostics.CodeAnalysis;
3+
using System.Xml;
4+
using System.Xml.Linq;
35

46
namespace Java.Interop.Tools.Generator
57
{
@@ -27,5 +29,17 @@ public static bool StartsWithAny (this string value, params string [] values)
2729
}
2830

2931
public static bool HasValue ([NotNullWhen (true)]this string? str) => !string.IsNullOrEmpty (str);
32+
33+
public static XDocument? LoadXmlDocument (string filename)
34+
{
35+
try {
36+
return XDocument.Load (filename, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
37+
} catch (XmlException e) {
38+
Report.Verbose (0, "Exception: {0}", e);
39+
Report.LogCodedWarning (0, Report.WarningInvalidXmlFile, e, filename, e.Message);
40+
}
41+
42+
return null;
43+
}
3044
}
3145
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Xml.Linq;
3+
using System.Xml.XPath;
4+
5+
namespace Xamarin.Android.Tools
6+
{
7+
static class XmlExtensions
8+
{
9+
public static string? XGetAttribute (this XElement element, string name)
10+
=> element.Attribute (name)?.Value.Trim ();
11+
12+
public static string? XGetAttribute (this XPathNavigator nav, string name, string ns)
13+
=> nav.GetAttribute (name, ns)?.Trim ();
14+
15+
public static int? XGetAttributeAsInt (this XElement element, string name)
16+
{
17+
var value = element.XGetAttribute (name);
18+
19+
if (int.TryParse (value, out var result))
20+
return result;
21+
22+
return null;
23+
}
24+
}
25+
}

src/Java.Interop.Tools.Generator/Java.Interop.Tools.Generator.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@
1515
<Compile Include="..\utils\NullableAttributes.cs" />
1616
</ItemGroup>
1717

18+
<ItemGroup>
19+
<ProjectReference Include="..\Java.Interop.Localization\Java.Interop.Localization.csproj" />
20+
</ItemGroup>
21+
1822
</Project>
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
using System;
2+
using System.Linq;
3+
using System.Xml.XPath;
4+
using System.Xml.Linq;
5+
6+
using Xamarin.Android.Tools;
7+
8+
namespace Java.Interop.Tools.Generator
9+
{
10+
public class FixupXmlDocument
11+
{
12+
public XDocument FixupDocument { get; }
13+
14+
public FixupXmlDocument (XDocument fixupDocument)
15+
{
16+
FixupDocument = fixupDocument;
17+
}
18+
19+
public static FixupXmlDocument? Load (string filename)
20+
{
21+
if (UtilityExtensions.LoadXmlDocument (filename) is XDocument doc)
22+
return new FixupXmlDocument (doc);
23+
24+
return null;
25+
}
26+
27+
public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int productVersion)
28+
{
29+
// Defaulting to 0 here is fine
30+
int.TryParse (apiLevelString, out var apiLevel);
31+
32+
var metadataChildren = FixupDocument.XPathSelectElements ("/metadata/*");
33+
34+
string? prev_path = null;
35+
XElement? attr_last_cache = null;
36+
37+
foreach (var metaitem in metadataChildren) {
38+
if (ShouldSkip (metaitem, apiLevel, productVersion))
39+
continue;
40+
if (!ShouldApply (metaitem, apiDocument))
41+
continue;
42+
43+
var path = metaitem.XGetAttribute ("path");
44+
45+
if (path != prev_path)
46+
attr_last_cache = null;
47+
48+
prev_path = path;
49+
50+
switch (metaitem.Name.LocalName) {
51+
case "remove-node":
52+
try {
53+
var nodes = apiDocument.ApiDocument.XPathSelectElements (path).ToArray ();
54+
55+
if (nodes.Any ())
56+
foreach (var node in nodes)
57+
node.Remove ();
58+
else
59+
// BG8A00
60+
Report.LogCodedWarning (0, Report.WarningRemoveNodeMatchedNoNodes, null, metaitem, $"<remove-node path=\"{path}\" />");
61+
} catch (XPathException e) {
62+
// BG4301
63+
Report.LogCodedError (Report.ErrorRemoveNodeInvalidXPath, e, metaitem, path);
64+
}
65+
break;
66+
case "add-node":
67+
try {
68+
var nodes = apiDocument.ApiDocument.XPathSelectElements (path);
69+
70+
if (!nodes.Any ())
71+
// BG8A01
72+
Report.LogCodedWarning (0, Report.WarningAddNodeMatchedNoNodes, null, metaitem, $"<add-node path=\"{path}\" />");
73+
else {
74+
foreach (var node in nodes)
75+
node.Add (metaitem.Nodes ());
76+
}
77+
} catch (XPathException e) {
78+
// BG4302
79+
Report.LogCodedError (Report.ErrorAddNodeInvalidXPath, e, metaitem, path);
80+
}
81+
break;
82+
case "change-node":
83+
try {
84+
var nodes = apiDocument.ApiDocument.XPathSelectElements (path);
85+
var matched = false;
86+
87+
foreach (var node in nodes) {
88+
var newChild = new XElement (metaitem.Value);
89+
newChild.Add (node.Attributes ());
90+
newChild.Add (node.Nodes ());
91+
node.ReplaceWith (newChild);
92+
matched = true;
93+
}
94+
95+
if (!matched)
96+
// BG8A03
97+
Report.LogCodedWarning (0, Report.WarningChangeNodeTypeMatchedNoNodes, null, metaitem, $"<change-node-type path=\"{path}\" />");
98+
} catch (XPathException e) {
99+
// BG4303
100+
Report.LogCodedError (Report.ErrorChangeNodeInvalidXPath, e, metaitem, path);
101+
}
102+
break;
103+
case "attr":
104+
try {
105+
var attr_name = metaitem.XGetAttribute ("name");
106+
107+
if (string.IsNullOrEmpty (attr_name))
108+
// BG4307
109+
Report.LogCodedError (Report.ErrorMissingAttrName, null, metaitem, path);
110+
var nodes = attr_last_cache != null ? new XElement [] { attr_last_cache } : apiDocument.ApiDocument.XPathSelectElements (path);
111+
var attr_matched = 0;
112+
113+
foreach (var n in nodes) {
114+
n.SetAttributeValue (attr_name, metaitem.Value);
115+
attr_matched++;
116+
}
117+
if (attr_matched == 0)
118+
// BG8A04
119+
Report.LogCodedWarning (0, Report.WarningAttrMatchedNoNodes, null, metaitem, $"<attr path=\"{path}\" />");
120+
if (attr_matched != 1)
121+
attr_last_cache = null;
122+
} catch (XPathException e) {
123+
// BG4304
124+
Report.LogCodedError (Report.ErrorAttrInvalidXPath, e, metaitem, path);
125+
}
126+
break;
127+
case "move-node":
128+
try {
129+
var parent = metaitem.Value;
130+
var parents = apiDocument.ApiDocument.XPathSelectElements (parent);
131+
var matched = false;
132+
133+
foreach (var parent_node in parents) {
134+
var nodes = parent_node.XPathSelectElements (path).ToArray ();
135+
foreach (var node in nodes)
136+
node.Remove ();
137+
parent_node.Add (nodes);
138+
matched = true;
139+
}
140+
if (!matched)
141+
// BG8A05
142+
Report.LogCodedWarning (0, Report.WarningMoveNodeMatchedNoNodes, null, metaitem, $"<move-node path=\"{path}\" />");
143+
} catch (XPathException e) {
144+
// BG4305
145+
Report.LogCodedError (Report.ErrorMoveNodeInvalidXPath, e, metaitem, path);
146+
}
147+
break;
148+
case "remove-attr":
149+
try {
150+
var name = metaitem.XGetAttribute ("name");
151+
var nodes = apiDocument.ApiDocument.XPathSelectElements (path);
152+
var matched = false;
153+
154+
foreach (var node in nodes) {
155+
node.RemoveAttributes ();
156+
matched = true;
157+
}
158+
159+
if (!matched)
160+
// BG8A06
161+
Report.LogCodedWarning (0, Report.WarningRemoveAttrMatchedNoNodes, null, metaitem, $"<remove-attr path=\"{path}\" />");
162+
} catch (XPathException e) {
163+
// BG4306
164+
Report.LogCodedError (Report.ErrorRemoveAttrInvalidXPath, e, metaitem, path);
165+
}
166+
break;
167+
}
168+
}
169+
}
170+
171+
bool ShouldSkip (XElement node, int apiLevel, int productVersion)
172+
{
173+
if (apiLevel > 0) {
174+
var since = node.XGetAttributeAsInt ("api-since");
175+
var until = node.XGetAttributeAsInt ("api-until");
176+
177+
if (since is int since_int && since_int > apiLevel)
178+
return true;
179+
else if (until is int until_int && until_int < apiLevel)
180+
return true;
181+
}
182+
183+
if (productVersion > 0) {
184+
var product_version = node.XGetAttributeAsInt ("product-version");
185+
186+
if (product_version is int version && version > productVersion)
187+
return true;
188+
189+
}
190+
return false;
191+
}
192+
193+
bool ShouldApply (XElement node, ApiXmlDocument apiDocument)
194+
{
195+
if (apiDocument.ApiSource.HasValue ()) {
196+
var targetsource = node.XGetAttribute ("api-source");
197+
198+
if (!targetsource.HasValue ())
199+
return true;
200+
201+
return targetsource == apiDocument.ApiSource;
202+
}
203+
204+
return true;
205+
}
206+
}
207+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Xml;
3+
using System.Xml.Linq;
4+
using Xamarin.Android.Tools;
5+
6+
namespace Java.Interop.Tools.Generator
7+
{
8+
public class ApiXmlDocument
9+
{
10+
public XDocument ApiDocument { get; }
11+
public string ApiLevel { get; }
12+
public int ProductVersion { get; }
13+
14+
public string? ApiSource => ApiDocument.Root?.XGetAttribute ("api-source");
15+
16+
ApiXmlDocument (XDocument document, string apiLevel, int productVersion)
17+
{
18+
ApiDocument = document;
19+
ApiLevel = apiLevel;
20+
ProductVersion = productVersion;
21+
}
22+
23+
public static ApiXmlDocument? Load (string filename, string apiLevel, int productVersion)
24+
{
25+
if (UtilityExtensions.LoadXmlDocument (filename) is XDocument doc)
26+
return new ApiXmlDocument (doc, apiLevel, productVersion);
27+
28+
return null;
29+
}
30+
31+
public void ApplyFixupFile (string filename)
32+
{
33+
try {
34+
if (FixupXmlDocument.Load (filename) is FixupXmlDocument fixup)
35+
fixup.Apply (this, ApiLevel, ProductVersion);
36+
} catch (XmlException ex) {
37+
// BG4200
38+
Report.LogCodedError (Report.ErrorFailedToProcessMetadata, ex.Message);
39+
}
40+
}
41+
}
42+
}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
62

7-
namespace MonoDroid.Generation
3+
namespace Java.Interop.Tools.Generator
84
{
95
public interface ISourceLineInfo
106
{

0 commit comments

Comments
 (0)