Skip to content

Commit c2f4169

Browse files
committed
[generator] Support [Obsolete]/[SupportedOSPlatform] attributes for enum members.
1 parent 149d70f commit c2f4169

File tree

3 files changed

+103
-21
lines changed

3 files changed

+103
-21
lines changed

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

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IO;
4+
using System.Linq;
45
using System.Reflection;
56
using System.Text;
67
using Java.Interop.Tools.Generator.Enumification;
78
using MonoDroid.Generation;
89
using NUnit.Framework;
910
using NUnit.Framework.Internal;
11+
using Xamarin.Android.Binder;
1012

1113
namespace generatortests
1214
{
1315
[TestFixture]
14-
class EnumGeneratorTests
16+
class EnumGeneratorTests : CodeGeneratorTestBase
1517
{
16-
protected EnumGenerator generator;
17-
protected StringBuilder builder;
18-
protected StringWriter writer;
18+
protected new EnumGenerator generator;
19+
20+
protected override CodeGenerationTarget Target => CodeGenerationTarget.XAJavaInterop1;
1921

2022
[SetUp]
21-
public void SetUp ()
23+
public new void SetUp ()
2224
{
2325
builder = new StringBuilder ();
2426
writer = new StringWriter (builder);
@@ -60,7 +62,78 @@ public void WriteEnumWithGens ()
6062
Assert.AreEqual (GetExpected (nameof (WriteEnumWithGens)), writer.ToString ().NormalizeLineEndings ());
6163
}
6264

63-
protected string GetExpected (string testName)
65+
[Test]
66+
public void ObsoletedOSPlatformAttributeSupport ()
67+
{
68+
var xml = @"<api>
69+
<package name='java.lang' jni-name='java/lang'>
70+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
71+
</package>
72+
<package name='android.app' jni-name='android/app'>
73+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
74+
<field deprecated='deprecated' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
75+
</class>
76+
</package>
77+
</api>";
78+
79+
options.UseObsoletedOSPlatformAttributes = true;
80+
81+
var enu = CreateEnum ();
82+
var gens = ParseApiDefinition (xml);
83+
84+
generator.WriteEnumeration (options, enu, gens.ToArray ());
85+
86+
// Ensure [ObsoletedOSPlatform] and [SupportedOSPlatform] are written
87+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Runtime.Versioning.SupportedOSPlatformAttribute(\"android30.0\")][global::System.Runtime.Versioning.ObsoletedOSPlatform(\"android31.0\")]WithExcluded=1"), writer.ToString ());
88+
}
89+
90+
[Test]
91+
public void ObsoleteAttributeSupport ()
92+
{
93+
var xml = @"<api>
94+
<package name='java.lang' jni-name='java/lang'>
95+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
96+
</package>
97+
<package name='android.app' jni-name='android/app'>
98+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
99+
<field deprecated='deprecated' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
100+
</class>
101+
</package>
102+
</api>";
103+
104+
var enu = CreateEnum ();
105+
var gens = ParseApiDefinition (xml);
106+
107+
generator.WriteEnumeration (options, enu, gens.ToArray ());
108+
109+
// Ensure [Obsolete] is written
110+
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]WithExcluded=1"), writer.ToString ());
111+
}
112+
113+
[Test]
114+
public void ObsoleteFieldButNotEnumAttributeSupport ()
115+
{
116+
var xml = @"<api>
117+
<package name='java.lang' jni-name='java/lang'>
118+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
119+
</package>
120+
<package name='android.app' jni-name='android/app'>
121+
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
122+
<field deprecated='This constant will be removed in the future version. Use Android.App.RecentTaskFlags enum directly instead of this field.' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
123+
</class>
124+
</package>
125+
</api>";
126+
127+
var enu = CreateEnum ();
128+
var gens = ParseApiDefinition (xml);
129+
130+
generator.WriteEnumeration (options, enu, gens.ToArray ());
131+
132+
// [Obsolete] should not be written because the value isn't deprecated, just the _field_ is deprecated because we want people to use the enum instead
133+
Assert.False (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]WithExcluded=1"), writer.ToString ());
134+
}
135+
136+
protected new string GetExpected (string testName)
64137
{
65138
var root = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);
66139

@@ -71,7 +144,7 @@ protected string GetExpected (string testName)
71144
{
72145
var enu = new EnumMappings.EnumDescription {
73146
Members = new List<ConstantEntry> {
74-
new ConstantEntry { EnumMember = "WithExcluded", Value = "1", JavaSignature = "android/app/ActivityManager.RECENT_IGNORE_UNAVAILABLE" },
147+
new ConstantEntry { EnumMember = "WithExcluded", Value = "1", JavaSignature = "android/app/ActivityManager.RECENT_IGNORE_UNAVAILABLE", ApiLevel = 30 },
75148
new ConstantEntry { EnumMember = "IgnoreUnavailable", Value = "2", JavaSignature = "android/app/ActivityManager.RECENT_WITH_EXCLUDED" }
76149
},
77150
BitField = false,

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,36 +50,43 @@ EnumWriter CreateWriter (CodeGenerationOptions opt, KeyValuePair<string, EnumDes
5050
Value = member.Value.Trim (),
5151
};
5252

53+
// Try to find the original field in our model
5354
var managedMember = FindManagedMember (enu.Value, member, gens);
55+
var managedMemberName = managedMember != null ? $"{managedMember.Value.Cls.FullName}.{managedMember.Value.Field.Name}" : null;
5456

5557
if (opt.CodeGenerationTarget != CodeGenerationTarget.JavaInterop1)
56-
m.Attributes.Add (new IntDefinitionAttr (managedMember, StripExtraInterfaceSpec (member.JavaSignature)));
58+
m.Attributes.Add (new IntDefinitionAttr (managedMemberName, StripExtraInterfaceSpec (member.JavaSignature)));
59+
60+
SourceWriterExtensions.AddSupportedOSPlatform (m.Attributes, member.ApiLevel, opt);
61+
62+
// Some of our source fields may have been marked with:
63+
// "This constant will be removed in the future version. Use XXX enum directly instead of this field."
64+
// We don't want this message to propogate to the enum.
65+
if (managedMember != null && !managedMember.Value.Field.DeprecatedComment.Contains ("enum directly instead of this field"))
66+
SourceWriterExtensions.AddObsolete (m.Attributes, managedMember.Value.Field.DeprecatedComment, opt, deprecatedSince: managedMember.Value.Field.DeprecatedSince);
5767

5868
enoom.Members.Add (m);
5969
}
6070

6171
return enoom;
6272
}
6373

64-
string FindManagedMember (EnumDescription desc, ConstantEntry member, IEnumerable<GenBase> gens)
74+
WeakReference cache_found_class;
75+
76+
(GenBase Cls, Field Field)? FindManagedMember (EnumDescription desc, ConstantEntry constant, IEnumerable<GenBase> gens)
6577
{
6678
if (desc.FieldsRemoved)
6779
return null;
6880

69-
var jniMember = member.JavaSignature;
81+
var jniMember = constant.JavaSignature;
82+
7083
if (string.IsNullOrWhiteSpace (jniMember)) {
7184
// enum values like "None" falls here.
7285
return null;
7386
}
74-
return FindManagedMember (jniMember, gens);
75-
}
7687

77-
WeakReference cache_found_class;
88+
ParseJniMember (jniMember, out var package, out var type, out var member);
7889

79-
string FindManagedMember (string jniMember, IEnumerable<GenBase> gens)
80-
{
81-
string package, type, member;
82-
ParseJniMember (jniMember, out package, out type, out member);
8390
var fullJavaType = (string.IsNullOrEmpty (package) ? "" : package + ".") + type;
8491

8592
var cls = cache_found_class != null ? cache_found_class.Target as GenBase : null;
@@ -96,7 +103,7 @@ string FindManagedMember (string jniMember, IEnumerable<GenBase> gens)
96103
// The field was not found e.g. removed by metadata fixup.
97104
return null;
98105
}
99-
return cls.FullName + "." + fld.Name;
106+
return (cls, fld);
100107
}
101108

102109
internal void ParseJniMember (string jniMember, out string package, out string type, out string member)

tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,14 @@ public static void AddParameterListCallArgs (List<string> body, ParameterList pa
293293
}
294294

295295
public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, ApiVersionsSupport.IApiAvailability member, CodeGenerationOptions opt)
296+
=> AddSupportedOSPlatform (attributes, member.ApiAvailableSince, opt);
297+
298+
public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, int since, CodeGenerationOptions opt)
296299
{
297300
// There's no sense in writing say 'android15' because we do not support older APIs,
298301
// so those APIs will be available in all of our versions.
299-
if (member.ApiAvailableSince > 21 && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
300-
attributes.Add (new SupportedOSPlatformAttr (member.ApiAvailableSince));
301-
302+
if (since > 21 && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
303+
attributes.Add (new SupportedOSPlatformAttr (since));
302304
}
303305

304306
public static void AddObsolete (List<AttributeWriter> attributes, string message, CodeGenerationOptions opt, bool forceDeprecate = false, bool isError = false, int? deprecatedSince = null)

0 commit comments

Comments
 (0)