-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[generator] Separate metadata fixup step from parsing step. (#822)
Context: #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.
- Loading branch information
Showing
29 changed files
with
591 additions
and
223 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
src/Java.Interop.Tools.Generator/Extensions/XmlExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System; | ||
using System.Globalization; | ||
using System.Xml.Linq; | ||
using System.Xml.XPath; | ||
|
||
namespace Xamarin.Android.Tools | ||
{ | ||
static class XmlExtensions | ||
{ | ||
public static string? XGetAttribute (this XElement element, string name) | ||
=> element.Attribute (name)?.Value.Trim (); | ||
|
||
public static string? XGetAttribute (this XPathNavigator nav, string name, string ns) | ||
=> nav.GetAttribute (name, ns)?.Trim (); | ||
|
||
public static int? XGetAttributeAsInt (this XElement element, string name) | ||
{ | ||
var value = element.XGetAttribute (name); | ||
|
||
if (int.TryParse (value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) | ||
return result; | ||
|
||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
207 changes: 207 additions & 0 deletions
207
src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Xml.XPath; | ||
using System.Xml.Linq; | ||
|
||
using Xamarin.Android.Tools; | ||
|
||
namespace Java.Interop.Tools.Generator | ||
{ | ||
public class FixupXmlDocument | ||
{ | ||
public XDocument FixupDocument { get; } | ||
|
||
public FixupXmlDocument (XDocument fixupDocument) | ||
{ | ||
FixupDocument = fixupDocument; | ||
} | ||
|
||
public static FixupXmlDocument? Load (string filename) | ||
{ | ||
if (UtilityExtensions.LoadXmlDocument (filename) is XDocument doc) | ||
return new FixupXmlDocument (doc); | ||
|
||
return null; | ||
} | ||
|
||
public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int productVersion) | ||
{ | ||
// Defaulting to 0 here is fine | ||
int.TryParse (apiLevelString, out var apiLevel); | ||
|
||
var metadataChildren = FixupDocument.XPathSelectElements ("/metadata/*"); | ||
|
||
string? prev_path = null; | ||
XElement? attr_last_cache = null; | ||
|
||
foreach (var metaitem in metadataChildren) { | ||
if (ShouldSkip (metaitem, apiLevel, productVersion)) | ||
continue; | ||
if (!ShouldApply (metaitem, apiDocument)) | ||
continue; | ||
|
||
var path = metaitem.XGetAttribute ("path"); | ||
|
||
if (path != prev_path) | ||
attr_last_cache = null; | ||
|
||
prev_path = path; | ||
|
||
switch (metaitem.Name.LocalName) { | ||
case "remove-node": | ||
try { | ||
var nodes = apiDocument.ApiDocument.XPathSelectElements (path).ToArray (); | ||
|
||
if (nodes.Any ()) | ||
foreach (var node in nodes) | ||
node.Remove (); | ||
else | ||
// BG8A00 | ||
Report.LogCodedWarning (0, Report.WarningRemoveNodeMatchedNoNodes, null, metaitem, $"<remove-node path=\"{path}\" />"); | ||
} catch (XPathException e) { | ||
// BG4301 | ||
Report.LogCodedError (Report.ErrorRemoveNodeInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
case "add-node": | ||
try { | ||
var nodes = apiDocument.ApiDocument.XPathSelectElements (path); | ||
|
||
if (!nodes.Any ()) | ||
// BG8A01 | ||
Report.LogCodedWarning (0, Report.WarningAddNodeMatchedNoNodes, null, metaitem, $"<add-node path=\"{path}\" />"); | ||
else { | ||
foreach (var node in nodes) | ||
node.Add (metaitem.Nodes ()); | ||
} | ||
} catch (XPathException e) { | ||
// BG4302 | ||
Report.LogCodedError (Report.ErrorAddNodeInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
case "change-node": | ||
try { | ||
var nodes = apiDocument.ApiDocument.XPathSelectElements (path); | ||
var matched = false; | ||
|
||
foreach (var node in nodes) { | ||
var newChild = new XElement (metaitem.Value); | ||
newChild.Add (node.Attributes ()); | ||
newChild.Add (node.Nodes ()); | ||
node.ReplaceWith (newChild); | ||
matched = true; | ||
} | ||
|
||
if (!matched) | ||
// BG8A03 | ||
Report.LogCodedWarning (0, Report.WarningChangeNodeTypeMatchedNoNodes, null, metaitem, $"<change-node-type path=\"{path}\" />"); | ||
} catch (XPathException e) { | ||
// BG4303 | ||
Report.LogCodedError (Report.ErrorChangeNodeInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
case "attr": | ||
try { | ||
var attr_name = metaitem.XGetAttribute ("name"); | ||
|
||
if (string.IsNullOrEmpty (attr_name)) | ||
// BG4307 | ||
Report.LogCodedError (Report.ErrorMissingAttrName, null, metaitem, path); | ||
var nodes = attr_last_cache != null ? new XElement [] { attr_last_cache } : apiDocument.ApiDocument.XPathSelectElements (path); | ||
var attr_matched = 0; | ||
|
||
foreach (var n in nodes) { | ||
n.SetAttributeValue (attr_name, metaitem.Value); | ||
attr_matched++; | ||
} | ||
if (attr_matched == 0) | ||
// BG8A04 | ||
Report.LogCodedWarning (0, Report.WarningAttrMatchedNoNodes, null, metaitem, $"<attr path=\"{path}\" />"); | ||
if (attr_matched != 1) | ||
attr_last_cache = null; | ||
} catch (XPathException e) { | ||
// BG4304 | ||
Report.LogCodedError (Report.ErrorAttrInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
case "move-node": | ||
try { | ||
var parent = metaitem.Value; | ||
var parents = apiDocument.ApiDocument.XPathSelectElements (parent); | ||
var matched = false; | ||
|
||
foreach (var parent_node in parents) { | ||
var nodes = parent_node.XPathSelectElements (path).ToArray (); | ||
foreach (var node in nodes) | ||
node.Remove (); | ||
parent_node.Add (nodes); | ||
matched = true; | ||
} | ||
if (!matched) | ||
// BG8A05 | ||
Report.LogCodedWarning (0, Report.WarningMoveNodeMatchedNoNodes, null, metaitem, $"<move-node path=\"{path}\" />"); | ||
} catch (XPathException e) { | ||
// BG4305 | ||
Report.LogCodedError (Report.ErrorMoveNodeInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
case "remove-attr": | ||
try { | ||
var name = metaitem.XGetAttribute ("name"); | ||
var nodes = apiDocument.ApiDocument.XPathSelectElements (path); | ||
var matched = false; | ||
|
||
foreach (var node in nodes) { | ||
node.RemoveAttributes (); | ||
matched = true; | ||
} | ||
|
||
if (!matched) | ||
// BG8A06 | ||
Report.LogCodedWarning (0, Report.WarningRemoveAttrMatchedNoNodes, null, metaitem, $"<remove-attr path=\"{path}\" />"); | ||
} catch (XPathException e) { | ||
// BG4306 | ||
Report.LogCodedError (Report.ErrorRemoveAttrInvalidXPath, e, metaitem, path); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
|
||
bool ShouldSkip (XElement node, int apiLevel, int productVersion) | ||
{ | ||
if (apiLevel > 0) { | ||
var since = node.XGetAttributeAsInt ("api-since"); | ||
var until = node.XGetAttributeAsInt ("api-until"); | ||
|
||
if (since is int since_int && since_int > apiLevel) | ||
return true; | ||
else if (until is int until_int && until_int < apiLevel) | ||
return true; | ||
} | ||
|
||
if (productVersion > 0) { | ||
var product_version = node.XGetAttributeAsInt ("product-version"); | ||
|
||
if (product_version is int version && version > productVersion) | ||
return true; | ||
|
||
} | ||
return false; | ||
} | ||
|
||
bool ShouldApply (XElement node, ApiXmlDocument apiDocument) | ||
{ | ||
if (apiDocument.ApiSource.HasValue ()) { | ||
var targetsource = node.XGetAttribute ("api-source"); | ||
|
||
if (!targetsource.HasValue ()) | ||
return true; | ||
|
||
return targetsource == apiDocument.ApiSource; | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
src/Java.Interop.Tools.Generator/Utilities/ApiXmlDocument.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System; | ||
using System.Xml; | ||
using System.Xml.Linq; | ||
using Xamarin.Android.Tools; | ||
|
||
namespace Java.Interop.Tools.Generator | ||
{ | ||
public class ApiXmlDocument | ||
{ | ||
public XDocument ApiDocument { get; } | ||
public string ApiLevel { get; } | ||
public int ProductVersion { get; } | ||
|
||
public string? ApiSource => ApiDocument.Root?.XGetAttribute ("api-source"); | ||
|
||
public ApiXmlDocument (XDocument document, string apiLevel, int productVersion) | ||
{ | ||
ApiDocument = document; | ||
ApiLevel = apiLevel; | ||
ProductVersion = productVersion; | ||
} | ||
|
||
public static ApiXmlDocument? Load (string filename, string apiLevel, int productVersion) | ||
{ | ||
if (UtilityExtensions.LoadXmlDocument (filename) is XDocument doc) | ||
return new ApiXmlDocument (doc, apiLevel, productVersion); | ||
|
||
return null; | ||
} | ||
|
||
public void ApplyFixupFile (string filename) | ||
{ | ||
if (FixupXmlDocument.Load (filename) is FixupXmlDocument fixup) | ||
ApplyFixupFile (fixup); | ||
} | ||
|
||
public void ApplyFixupFile (FixupXmlDocument fixup) | ||
{ | ||
try { | ||
fixup.Apply (this, ApiLevel, ProductVersion); | ||
} catch (XmlException ex) { | ||
// BG4200 | ||
Report.LogCodedError (Report.ErrorFailedToProcessMetadata, ex.Message); | ||
} | ||
} | ||
} | ||
} |
6 changes: 1 addition & 5 deletions
6
....Generator.ObjectModel/ISourceLineInfo.cs → ...ls.Generator/Utilities/ISourceLineInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.