Skip to content

Commit c925b78

Browse files
authored
[generator] Support varargs arrays in event listener methods (#832)
Fixes: #817 Assume you have a Java event listener interface which accepts a Java [varargs array][0], e.g.: // Java public interface SomeListener { void onSomeNotification(long a, Object... b); } public class Whatever { public void setSomeListener(SomeListener listener) {…} } then `SomeListener` and `Whatever` are bound as expected: // C# binding public partial interface ISomeListener { void OnSomeNotification(long a, params Object[] b); } public partial class Whatever { public void SetSomeListener(ISomeListener listener) {…} } *and also*, we ["eventify"][1] the listener interface members, wherein the listener interface members become events on the using type, with their `on` prefix removed: public partial class Whatever { public event EventHandler<SomeNotificationEventArgs> SomeNotification {…} } All parameters on the listener method become constructor parameters on the `EventArgs` subclass: public partial class SomeNotificationEventArgs : EventArgs { public SomeNotificationEventArgs(long a, params Object[] b); } Those parameters also become readonly properties: partial class SomeNotificationEventArgs { public long A {get;} } The bug is that when Java varargs arrays are used, the C# `params` keyword remains on the declared `EventArgs` subclass property: partial class SomeNotificationEventArgs { public params Object[] B {get;} } Which results in a CS1519 error: error CS1519: Invalid token 'params' in class, struct, or interface member declaration Fix the CS1519 by removing the extraneous `params` output: partial class SomeNotificationEventArgs { public Object[] B {get;} } [0]: https://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html [1]: https://docs.microsoft.com/en-us/xamarin/android/internals/api-design#events-and-listeners
1 parent 4d0cba6 commit c925b78

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// event args for com.xamarin.android.MyListener.onDoSomething
2+
public partial class MyEventArgs : global::System.EventArgs {
3+
4+
public MyEventArgs (params Java.Lang.Object[] args)
5+
{
6+
this.args = args;
7+
}
8+
9+
Java.Lang.Object[] args;
10+
public Java.Lang.Object[] Args {
11+
get { return args; }
12+
}
13+
}
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// event args for com.xamarin.android.MyListener.onDoSomething
2+
public partial class MyEventArgs : global::System.EventArgs {
3+
4+
public MyEventArgs (params Java.Lang.Object[]? args)
5+
{
6+
this.args = args;
7+
}
8+
9+
Java.Lang.Object[]? args;
10+
public Java.Lang.Object[]? Args {
11+
get { return args; }
12+
}
13+
}
14+

tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,32 @@ public void WriteInterfaceEventArgs ()
10401040
Assert.AreEqual (GetExpected (nameof (WriteInterfaceEventArgs)), writer.ToString ().NormalizeLineEndings ());
10411041
}
10421042

1043+
[Test]
1044+
public void WriteInterfaceEventArgsWithParamArray ()
1045+
{
1046+
var xml = @"<api>
1047+
<package name='java.lang' jni-name='java/lang'>
1048+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
1049+
</package>
1050+
<package name='com.xamarin.android' jni-name='com/xamarin/android'>
1051+
<interface abstract='true' deprecated='not deprecated' final='false' name='MyListener' static='true' visibility='public' jni-signature='Lcom/xamarin/android/MyListener;'>
1052+
<method abstract='true' deprecated='deprecated' final='false' name='onDoSomething' jni-signature='([Ljava/lang/Object;)V' bridge='false' native='false' return='void' jni-return='V' static='false' synchronized='false' synthetic='false' visibility='public'>
1053+
<parameter name='args' type='java.lang.Object...' jni-type='[Ljava/lang/Object;'></parameter>
1054+
</method>
1055+
</interface>
1056+
</package>
1057+
</api>";
1058+
1059+
var gens = ParseApiDefinition (xml);
1060+
var iface = gens.Single (g => g.Name == "IMyListener");
1061+
1062+
generator.Context.ContextTypes.Push (iface);
1063+
generator.WriteInterfaceEventArgs (iface as InterfaceGen, iface.Methods [0], string.Empty);
1064+
generator.Context.ContextTypes.Pop ();
1065+
1066+
Assert.AreEqual (GetExpected (nameof (WriteInterfaceEventArgsWithParamArray)), writer.ToString ().NormalizeLineEndings ());
1067+
}
1068+
10431069
[Test]
10441070
public void WriteInterfaceEventHandler ()
10451071
{

tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -598,10 +598,14 @@ public void WriteInterfaceEventArgs (InterfaceGen @interface, Method m, string i
598598
if (p.IsSender)
599599
continue;
600600
writer.WriteLine ();
601-
//var safeTypeName = p.Type.StartsWith ("params ", StringComparison.Ordinal) ? p.Type.Substring ("params ".Length) : p.Type;
602-
writer.WriteLine ("{0}\t{1} {2};", indent, opt.GetTypeReferenceName (p), opt.GetSafeIdentifier (p.Name));
601+
602+
// Remove "params" from things like "global::Java.Lang.Object[]"
603+
var type_reference_name = opt.GetTypeReferenceName (p);
604+
type_reference_name = type_reference_name.StartsWith ("params ", StringComparison.Ordinal) ? type_reference_name.Substring ("params ".Length) : type_reference_name;
605+
606+
writer.WriteLine ("{0}\t{1} {2};", indent, type_reference_name, opt.GetSafeIdentifier (p.Name));
603607
// AbsListView.IMultiChoiceModeListener.onItemCheckedStateChanged() hit this strict name check, at parameter "@checked".
604-
writer.WriteLine ("{0}\tpublic {1} {2} {{", indent, opt.GetTypeReferenceName (p), p.PropertyName);
608+
writer.WriteLine ("{0}\tpublic {1} {2} {{", indent, type_reference_name, p.PropertyName);
605609
writer.WriteLine ("{0}\t\tget {{ return {1}; }}", indent, opt.GetSafeIdentifier (p.Name));
606610
writer.WriteLine ("{0}\t}}", indent);
607611
}

0 commit comments

Comments
 (0)