Skip to content

Commit

Permalink
[generator] Support XML defined enums with no JNI info (#659)
Browse files Browse the repository at this point in the history
Fixes: #515

Our `.csv` format for specifying enums allows creating enum fields that
do not map to a JNI field.  This is often used to add a `None` enum
field to a Java `[Flags]` enum:

	0,,0,Android.AccessibilityServices.AccessibilityServiceCapabilities,None

However our XML format does not support this because it considers the
(`//mapping/@jni-class` or `//mapping/@jni-interface`) and
`//mapping/@jni-name` attributes to be mandatory when translating the
XML format to `.csv` format.

We can remove this restriction and correctly generate the needed `.csv`
format so that users using the XML format can add arbitrary enums that
`generator` knows about, permitting:

	<mapping clr-enum-type='Android.Support.V4.App.FragmentTagType' bitfield='true'>
	  <field clr-name='Name' value='0' />
	  <field clr-name='Id' value='1' />
	  <field clr-name='Tag' value='2' />
	</mapping>

in addition to the previously required:

	<mapping jni-interface='android/support/v4/app/FragmentActivity$FragmentTag' clr-enum-type='Android.Support.V4.App.FragmentTagType' bitfield='true'>
	  <field jni-name='Fragment_name' clr-name='Name' value='0' />
	  <field jni-name='Fragment_id' clr-name='Id' value='1' />
	  <field jni-name='Fragment_tag' clr-name='Tag' value='2' />
	</mapping>
  • Loading branch information
jpobst authored Jun 9, 2020
1 parent 5dcf896 commit 267c3f3
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 15 deletions.
38 changes: 23 additions & 15 deletions src/utils/EnumMappings.Xml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,33 @@ internal static TextReader FieldXmlToCsv (string file)
if (file == null)
return null;

return FieldXmlToCsv (XDocument.Load (file, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo));
}

internal static TextReader FieldXmlToCsv (XDocument doc)
{
var sw = new StringWriter ();
var doc = XDocument.Load (file, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);

foreach (var e in doc.XPathSelectElements ("/enum-field-mappings/mapping")) {
string enu = GetMandatoryAttribute (e, "clr-enum-type");
string jni_type = e.Attribute ("jni-class") != null
? e.XGetAttribute ("jni-class")
: e.Attribute ("jni-interface") != null
? "I:" + e.XGetAttribute ("jni-interface")
: GetMandatoryAttribute (e, "jni-class or jni-interface");
bool bitfield = e.Attribute ("bitfield") != null && e.XGetAttribute ("bitfield") == "true";

var enu = GetMandatoryAttribute (e, "clr-enum-type");
var jni_type = e.XGetAttribute ("jni-class") ?? "I:" + e.XGetAttribute ("jni-interface");

// If neither jni was specified leave it blank
if (jni_type == "I:")
jni_type = string.Empty;

var bitfield = e.XGetAttribute ("bitfield") == "true";

foreach (var m in e.XPathSelectElements ("field")) {
string verstr = m.Attribute ("api-level") != null
? m.XGetAttribute ("api-level")
: "0";
string member = GetMandatoryAttribute (m, "clr-name");
string jni_name = GetMandatoryAttribute (m, "jni-name");
string value = GetMandatoryAttribute (m, "value");
sw.WriteLine ("{0}, {1}, {2}, {3}, {4}{5}", verstr, enu, member, jni_type + '.' + jni_name, value, bitfield ? ", Flags" : null);
var verstr = m.XGetAttribute ("api-level") ?? "0";
var member = GetMandatoryAttribute (m, "clr-name");
var jni_name = m.XGetAttribute ("jni-name");
var value = GetMandatoryAttribute (m, "value");

var jni_member = string.IsNullOrWhiteSpace (jni_name) ? string.Empty : jni_type + '.' + jni_name;

sw.WriteLine ("{0}, {1}, {2}, {3}, {4}{5}", verstr, enu, member, jni_member, value, bitfield ? ", Flags" : null);
}
}

Expand Down
97 changes: 97 additions & 0 deletions tests/generator-Tests/Unit-Tests/EnumMappingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using MonoDroid.Generation;
using NUnit.Framework;

Expand Down Expand Up @@ -284,5 +285,101 @@ public void TransientEnumificationV2Test ()
Assert.AreEqual ("[Cdsect, I:org/xmlpull/v1/XmlPullParser.CDSECT]", enums.First ().Value.JniNames.Single ().ToString ());
Assert.AreEqual ("[Cdsect, 5]", enums.First ().Value.Members.Single ().ToString ());
}

[Test]
public void XmlEnumMapWithJNI ()
{
var xml = @"<enum-field-mappings>
<mapping jni-class='android/support/v4/app/FragmentActivity$FragmentTag' clr-enum-type='Android.Support.V4.App.FragmentTagType'>
<field jni-name='Fragment_name' clr-name='Name' value='0' />
<field jni-name='Fragment_id' clr-name='Id' value='1' />
<field jni-name='Fragment_tag' clr-name='Tag' value='2' />
</mapping>
</enum-field-mappings>";

var doc = XDocument.Parse (xml, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
var sr = EnumMappings.FieldXmlToCsv (doc);

var lines = sr.ReadToEnd ().Split (new [] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var expected = new [] {
"0, Android.Support.V4.App.FragmentTagType, Name, android/support/v4/app/FragmentActivity$FragmentTag.Fragment_name, 0",
"0, Android.Support.V4.App.FragmentTagType, Id, android/support/v4/app/FragmentActivity$FragmentTag.Fragment_id, 1",
"0, Android.Support.V4.App.FragmentTagType, Tag, android/support/v4/app/FragmentActivity$FragmentTag.Fragment_tag, 2"
};

Assert.AreEqual (expected, lines);
}

[Test]
public void XmlEnumMapWithInterfaceJNI ()
{
var xml = @"<enum-field-mappings>
<mapping jni-interface='android/support/v4/app/FragmentActivity$FragmentTag' clr-enum-type='Android.Support.V4.App.FragmentTagType' bitfield='true'>
<field jni-name='Fragment_name' clr-name='Name' value='0' />
<field jni-name='Fragment_id' clr-name='Id' value='1' />
<field jni-name='Fragment_tag' clr-name='Tag' value='2' />
</mapping>
</enum-field-mappings>";

var doc = XDocument.Parse (xml, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
var sr = EnumMappings.FieldXmlToCsv (doc);

var lines = sr.ReadToEnd ().Split (new [] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var expected = new [] {
"0, Android.Support.V4.App.FragmentTagType, Name, I:android/support/v4/app/FragmentActivity$FragmentTag.Fragment_name, 0, Flags",
"0, Android.Support.V4.App.FragmentTagType, Id, I:android/support/v4/app/FragmentActivity$FragmentTag.Fragment_id, 1, Flags",
"0, Android.Support.V4.App.FragmentTagType, Tag, I:android/support/v4/app/FragmentActivity$FragmentTag.Fragment_tag, 2, Flags"
};

Assert.AreEqual (expected, lines);
}

[Test]
public void XmlEnumMapWithoutJNI ()
{
var xml = @"<enum-field-mappings>
<mapping clr-enum-type='Android.Support.V4.App.FragmentTagType' bitfield='true'>
<field clr-name='Name' value='0' />
<field clr-name='Id' value='1' />
<field clr-name='Tag' value='2' />
</mapping>
</enum-field-mappings>";

var doc = XDocument.Parse (xml, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
var sr = EnumMappings.FieldXmlToCsv (doc);

var lines = sr.ReadToEnd ().Split (new [] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var expected = new [] {
"0, Android.Support.V4.App.FragmentTagType, Name, , 0, Flags",
"0, Android.Support.V4.App.FragmentTagType, Id, , 1, Flags",
"0, Android.Support.V4.App.FragmentTagType, Tag, , 2, Flags"
};

Assert.AreEqual (expected, lines);
}

[Test]
public void XmlEnumMapWithMixedJNI ()
{
var xml = @"<enum-field-mappings>
<mapping jni-class='android/support/v4/app/FragmentActivity$FragmentTag' clr-enum-type='Android.Support.V4.App.FragmentTagType' bitfield='true'>
<field clr-name='Name' value='0' />
<field jni-name='Fragment_id' clr-name='Id' value='1' />
<field clr-name='Tag' value='2' />
</mapping>
</enum-field-mappings>";

var doc = XDocument.Parse (xml, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
var sr = EnumMappings.FieldXmlToCsv (doc);

var lines = sr.ReadToEnd ().Split (new [] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
var expected = new [] {
"0, Android.Support.V4.App.FragmentTagType, Name, , 0, Flags",
"0, Android.Support.V4.App.FragmentTagType, Id, android/support/v4/app/FragmentActivity$FragmentTag.Fragment_id, 1, Flags",
"0, Android.Support.V4.App.FragmentTagType, Tag, , 2, Flags"
};

Assert.AreEqual (expected, lines);
}
}
}

0 comments on commit 267c3f3

Please sign in to comment.