Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit f4e68b5

Browse files
authored
[generator] Separate metadata fixup step from parsing step. (#822)
Context: https://github.com/xamarin/java.interop/issues/789 One issue with the proposal to flip the order of `ApiXmlAdjuster` and `metadata` in Issue #789 is that the "apply metadata" step is intertwined with the "parse API xml document into POCO's" step. This means that the adjuster step cannot be inserted between them, as the code is currently written. Separate out the two steps so that they can be reordered in the future. Additionally, refactor the metadata fixup step so that it can be unit tested more easily by moving it to `Java.Interop.Tools.Generator.dll`. Unit tests for applying metadata have been added.
1 parent f9faaab commit f4e68b5

29 files changed

Lines changed: 591 additions & 223 deletions

File tree

‎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
usingSystem;
22
usingSystem.Diagnostics.CodeAnalysis;
3+
usingSystem.Xml;
4+
usingSystem.Xml.Linq;
35

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

2931
publicstaticboolHasValue([NotNullWhen(true)]thisstring?str)=>!string.IsNullOrEmpty(str);
32+
33+
publicstaticXDocument?LoadXmlDocument(stringfilename)
34+
{
35+
try{
36+
returnXDocument.Load(filename,LoadOptions.SetBaseUri|LoadOptions.SetLineInfo);
37+
}catch(XmlExceptione){
38+
Report.Verbose(0,"Exception: {0}",e);
39+
Report.LogCodedWarning(0,Report.WarningInvalidXmlFile,e,filename,e.Message);
40+
}
41+
42+
returnnull;
43+
}
3044
}
3145
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
usingSystem;
2+
usingSystem.Globalization;
3+
usingSystem.Xml.Linq;
4+
usingSystem.Xml.XPath;
5+
6+
namespaceXamarin.Android.Tools
7+
{
8+
staticclassXmlExtensions
9+
{
10+
publicstaticstring?XGetAttribute(thisXElementelement,stringname)
11+
=>element.Attribute(name)?.Value.Trim();
12+
13+
publicstaticstring?XGetAttribute(thisXPathNavigatornav,stringname,stringns)
14+
=>nav.GetAttribute(name,ns)?.Trim();
15+
16+
publicstaticint?XGetAttributeAsInt(thisXElementelement,stringname)
17+
{
18+
varvalue=element.XGetAttribute(name);
19+
20+
if(int.TryParse(value,NumberStyles.Integer,CultureInfo.InvariantCulture,outvarresult))
21+
returnresult;
22+
23+
returnnull;
24+
}
25+
}
26+
}

‎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
<CompileInclude="..\utils\NullableAttributes.cs" />
1616
</ItemGroup>
1717

18+
<ItemGroup>
19+
<ProjectReferenceInclude="..\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+
usingSystem;
2+
usingSystem.Linq;
3+
usingSystem.Xml.XPath;
4+
usingSystem.Xml.Linq;
5+
6+
usingXamarin.Android.Tools;
7+
8+
namespaceJava.Interop.Tools.Generator
9+
{
10+
publicclassFixupXmlDocument
11+
{
12+
publicXDocumentFixupDocument{get;}
13+
14+
publicFixupXmlDocument(XDocumentfixupDocument)
15+
{
16+
FixupDocument=fixupDocument;
17+
}
18+
19+
publicstaticFixupXmlDocument?Load(stringfilename)
20+
{
21+
if(UtilityExtensions.LoadXmlDocument(filename)isXDocumentdoc)
22+
returnnewFixupXmlDocument(doc);
23+
24+
returnnull;
25+
}
26+
27+
publicvoidApply(ApiXmlDocumentapiDocument,stringapiLevelString,intproductVersion)
28+
{
29+
// Defaulting to 0 here is fine
30+
int.TryParse(apiLevelString,outvarapiLevel);
31+
32+
varmetadataChildren=FixupDocument.XPathSelectElements("/metadata/*");
33+
34+
string?prev_path=null;
35+
XElement?attr_last_cache=null;
36+
37+
foreach(varmetaiteminmetadataChildren){
38+
if(ShouldSkip(metaitem,apiLevel,productVersion))
39+
continue;
40+
if(!ShouldApply(metaitem,apiDocument))
41+
continue;
42+
43+
varpath=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+
varnodes=apiDocument.ApiDocument.XPathSelectElements(path).ToArray();
54+
55+
if(nodes.Any())
56+
foreach(varnodeinnodes)
57+
node.Remove();
58+
else
59+
// BG8A00
60+
Report.LogCodedWarning(0,Report.WarningRemoveNodeMatchedNoNodes,null,metaitem,$"<remove-node path=\"{path}\" />");
61+
}catch(XPathExceptione){
62+
// BG4301
63+
Report.LogCodedError(Report.ErrorRemoveNodeInvalidXPath,e,metaitem,path);
64+
}
65+
break;
66+
case"add-node":
67+
try{
68+
varnodes=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(varnodeinnodes)
75+
node.Add(metaitem.Nodes());
76+
}
77+
}catch(XPathExceptione){
78+
// BG4302
79+
Report.LogCodedError(Report.ErrorAddNodeInvalidXPath,e,metaitem,path);
80+
}
81+
break;
82+
case"change-node":
83+
try{
84+
varnodes=apiDocument.ApiDocument.XPathSelectElements(path);
85+
varmatched=false;
86+
87+
foreach(varnodeinnodes){
88+
varnewChild=newXElement(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(XPathExceptione){
99+
// BG4303
100+
Report.LogCodedError(Report.ErrorChangeNodeInvalidXPath,e,metaitem,path);
101+
}
102+
break;
103+
case"attr":
104+
try{
105+
varattr_name=metaitem.XGetAttribute("name");
106+
107+
if(string.IsNullOrEmpty(attr_name))
108+
// BG4307
109+
Report.LogCodedError(Report.ErrorMissingAttrName,null,metaitem,path);
110+
varnodes=attr_last_cache!=null?newXElement[]{attr_last_cache}:apiDocument.ApiDocument.XPathSelectElements(path);
111+
varattr_matched=0;
112+
113+
foreach(varninnodes){
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(XPathExceptione){
123+
// BG4304
124+
Report.LogCodedError(Report.ErrorAttrInvalidXPath,e,metaitem,path);
125+
}
126+
break;
127+
case"move-node":
128+
try{
129+
varparent=metaitem.Value;
130+
varparents=apiDocument.ApiDocument.XPathSelectElements(parent);
131+
varmatched=false;
132+
133+
foreach(varparent_nodeinparents){
134+
varnodes=parent_node.XPathSelectElements(path).ToArray();
135+
foreach(varnodeinnodes)
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(XPathExceptione){
144+
// BG4305
145+
Report.LogCodedError(Report.ErrorMoveNodeInvalidXPath,e,metaitem,path);
146+
}
147+
break;
148+
case"remove-attr":
149+
try{
150+
varname=metaitem.XGetAttribute("name");
151+
varnodes=apiDocument.ApiDocument.XPathSelectElements(path);
152+
varmatched=false;
153+
154+
foreach(varnodeinnodes){
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(XPathExceptione){
163+
// BG4306
164+
Report.LogCodedError(Report.ErrorRemoveAttrInvalidXPath,e,metaitem,path);
165+
}
166+
break;
167+
}
168+
}
169+
}
170+
171+
boolShouldSkip(XElementnode,intapiLevel,intproductVersion)
172+
{
173+
if(apiLevel>0){
174+
varsince=node.XGetAttributeAsInt("api-since");
175+
varuntil=node.XGetAttributeAsInt("api-until");
176+
177+
if(sinceisintsince_int&&since_int>apiLevel)
178+
returntrue;
179+
elseif(untilisintuntil_int&&until_int<apiLevel)
180+
returntrue;
181+
}
182+
183+
if(productVersion>0){
184+
varproduct_version=node.XGetAttributeAsInt("product-version");
185+
186+
if(product_versionisintversion&&version>productVersion)
187+
returntrue;
188+
189+
}
190+
returnfalse;
191+
}
192+
193+
boolShouldApply(XElementnode,ApiXmlDocumentapiDocument)
194+
{
195+
if(apiDocument.ApiSource.HasValue()){
196+
vartargetsource=node.XGetAttribute("api-source");
197+
198+
if(!targetsource.HasValue())
199+
returntrue;
200+
201+
returntargetsource==apiDocument.ApiSource;
202+
}
203+
204+
returntrue;
205+
}
206+
}
207+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
usingSystem;
2+
usingSystem.Xml;
3+
usingSystem.Xml.Linq;
4+
usingXamarin.Android.Tools;
5+
6+
namespaceJava.Interop.Tools.Generator
7+
{
8+
publicclassApiXmlDocument
9+
{
10+
publicXDocumentApiDocument{get;}
11+
publicstringApiLevel{get;}
12+
publicintProductVersion{get;}
13+
14+
publicstring?ApiSource=>ApiDocument.Root?.XGetAttribute("api-source");
15+
16+
publicApiXmlDocument(XDocumentdocument,stringapiLevel,intproductVersion)
17+
{
18+
ApiDocument=document;
19+
ApiLevel=apiLevel;
20+
ProductVersion=productVersion;
21+
}
22+
23+
publicstaticApiXmlDocument?Load(stringfilename,stringapiLevel,intproductVersion)
24+
{
25+
if(UtilityExtensions.LoadXmlDocument(filename)isXDocumentdoc)
26+
returnnewApiXmlDocument(doc,apiLevel,productVersion);
27+
28+
returnnull;
29+
}
30+
31+
publicvoidApplyFixupFile(stringfilename)
32+
{
33+
if(FixupXmlDocument.Load(filename)isFixupXmlDocumentfixup)
34+
ApplyFixupFile(fixup);
35+
}
36+
37+
publicvoidApplyFixupFile(FixupXmlDocumentfixup)
38+
{
39+
try{
40+
fixup.Apply(this,ApiLevel,ProductVersion);
41+
}catch(XmlExceptionex){
42+
// BG4200
43+
Report.LogCodedError(Report.ErrorFailedToProcessMetadata,ex.Message);
44+
}
45+
}
46+
}
47+
}

tools/generator/Java.Interop.Tools.Generator.ObjectModel/ISourceLineInfo.cs renamed to src/Java.Interop.Tools.Generator/Utilities/ISourceLineInfo.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
usingSystem;
2-
usingSystem.Collections.Generic;
3-
usingSystem.Linq;
4-
usingSystem.Text;
5-
usingSystem.Threading.Tasks;
62

7-
namespaceMonoDroid.Generation
3+
namespaceJava.Interop.Tools.Generator
84
{
95
publicinterfaceISourceLineInfo
106
{

0 commit comments

Comments
 (0)