Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Xml;
using System.Xml.Linq;

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

public static bool HasValue ([NotNullWhen (true)]this string? str) => !string.IsNullOrEmpty (str);

public static XDocument? LoadXmlDocument (string filename)
{
try {
return XDocument.Load (filename, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
} catch (XmlException e) {
Report.Verbose (0, "Exception: {0}", e);
Report.LogCodedWarning (0, Report.WarningInvalidXmlFile, e, filename, e.Message);
}

return null;
}
}
}
26 changes: 26 additions & 0 deletions src/Java.Interop.Tools.Generator/Extensions/XmlExtensions.cs
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
<Compile Include="..\utils\NullableAttributes.cs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Java.Interop.Localization\Java.Interop.Localization.csproj" />
</ItemGroup>

</Project>
207 changes: 207 additions & 0 deletions src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs
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 src/Java.Interop.Tools.Generator/Utilities/ApiXmlDocument.cs
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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MonoDroid.Generation
namespace Java.Interop.Tools.Generator
{
public interface ISourceLineInfo
{
Expand Down
Loading