From 38f96989cfca8e861329c6f47f0ef92e4468990e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 5 Jan 2023 09:47:05 -0600 Subject: [PATCH 1/4] [generator] Allow a constant map to explicitly set `deprecated-since` for an enum value. --- .../Enumification/ConstantEntry.cs | 7 +++-- .../Utilities/CsvParser.cs | 10 +++++++ .../Enumification/ConstantEntryTests.cs | 5 +++- .../Unit-Tests/EnumGeneratorTests.cs | 27 +++++++++++++++++++ .../EnumGenerator.cs | 12 ++++++--- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs b/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs index 9de1e02f5..9fa35ae99 100644 --- a/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs +++ b/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs @@ -16,6 +16,7 @@ public class ConstantEntry public string? EnumMember { get; set; } public FieldAction FieldAction { get; set; } public bool IsFlags { get; set; } + public int? DeprecatedSince { get; set; } public string EnumNamespace { get { @@ -133,7 +134,8 @@ static ConstantEntry FromVersion2String (CsvParser parser) EnumFullType = parser.GetField (4), EnumMember = parser.GetField (5), FieldAction = FromFieldActionString (parser.GetField (6)), - IsFlags = parser.GetField (7).ToLowerInvariant () == "flags" + IsFlags = parser.GetField (7).ToLowerInvariant () == "flags", + DeprecatedSince = parser.GetFieldAsIntNullable (8) }; entry.NormalizeJavaSignature (); @@ -175,7 +177,8 @@ public string ToVersion2String () EnumFullType, EnumMember, ToConstantFieldActionString (FieldAction), - IsFlags ? "flags" : string.Empty + IsFlags ? "flags" : string.Empty, + DeprecatedSince.HasValue ? DeprecatedSince.Value.ToString () : string.Empty, }; return string.Join (",", fields); diff --git a/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs b/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs index 66e2ecd8f..bfb463ef0 100644 --- a/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs +++ b/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs @@ -23,5 +23,15 @@ public int GetFieldAsInt (int index) { return int.Parse (GetField (index)); } + + public int? GetFieldAsIntNullable (int index) + { + var value = GetField (index); + + if (int.TryParse (value, out var val)) + return val; + + return default; + } } } diff --git a/tests/Java.Interop.Tools.Generator-Tests/Enumification/ConstantEntryTests.cs b/tests/Java.Interop.Tools.Generator-Tests/Enumification/ConstantEntryTests.cs index 6a46b828e..de2503567 100644 --- a/tests/Java.Interop.Tools.Generator-Tests/Enumification/ConstantEntryTests.cs +++ b/tests/Java.Interop.Tools.Generator-Tests/Enumification/ConstantEntryTests.cs @@ -84,6 +84,7 @@ public void ParseEnumMapV2 () Assert.AreEqual ("Cdsect", entry.EnumMember); Assert.AreEqual (FieldAction.Keep, entry.FieldAction); Assert.False (entry.IsFlags); + Assert.IsNull (entry.DeprecatedSince); } [Test] @@ -99,13 +100,14 @@ public void ParseAddEnumMapV2 () Assert.AreEqual ("Org.XmlPull.V1.XmlPullParserNode", entry.EnumFullType); Assert.AreEqual ("Cdsect", entry.EnumMember); Assert.AreEqual (FieldAction.None, entry.FieldAction); + Assert.IsNull (entry.DeprecatedSince); Assert.False (entry.IsFlags); } [Test] public void ParseRemoveEnumMapV2 () { - var csv = "R,10,I:org/xmlpull/v1/XmlPullParser.CDSECT,5,,,remove"; + var csv = "R,10,I:org/xmlpull/v1/XmlPullParser.CDSECT,5,,,remove,,33"; var entry = ConstantEntry.FromString (csv); Assert.AreEqual (ConstantAction.Remove, entry.Action); @@ -116,6 +118,7 @@ public void ParseRemoveEnumMapV2 () Assert.AreEqual (string.Empty, entry.EnumMember); Assert.AreEqual (FieldAction.Remove, entry.FieldAction); Assert.False (entry.IsFlags); + Assert.AreEqual (33, entry.DeprecatedSince.Value); } } } diff --git a/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs index 3053258ff..029a21c56 100644 --- a/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs @@ -133,6 +133,33 @@ public void ObsoleteFieldButNotEnumAttributeSupport () Assert.False (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]WithExcluded=1"), writer.ToString ()); } + [Test] + public void ObsoletedOSPlatformAttributeOverrideSupport () + { + var xml = @" + + + + + + + + + "; + + options.UseObsoletedOSPlatformAttributes = true; + + var enu = CreateEnum (); + enu.Value.Members.Single (m => m.EnumMember == "WithExcluded").DeprecatedSince = 33; + + var gens = ParseApiDefinition (xml); + + generator.WriteEnumeration (options, enu, gens.ToArray ()); + + // The field itself is not deprecated, but [ObsoletedOSPlatform] should be written because we set `DeprecatedSince` on the enum map + Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform(\"android33.0\")]WithExcluded=1"), writer.ToString ()); + } + protected new string GetExpected (string testName) { var root = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location); diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs index d159d3fcb..2e3f6da98 100644 --- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs +++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs @@ -59,11 +59,15 @@ EnumWriter CreateWriter (CodeGenerationOptions opt, KeyValuePair Date: Thu, 5 Jan 2023 15:47:10 -0600 Subject: [PATCH 2/4] Address feedback. --- .../Enumification/ConstantEntry.cs | 2 +- .../Enumification/ConstantsParser.cs | 14 ++++++++++ .../Utilities/CsvParser.cs | 2 +- .../Unit-Tests/EnumGeneratorTests.cs | 27 +++++++++++++++++++ .../EnumGenerator.cs | 3 ++- 5 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs b/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs index 9fa35ae99..9ad723054 100644 --- a/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs +++ b/src/Java.Interop.Tools.Generator/Enumification/ConstantEntry.cs @@ -135,7 +135,7 @@ static ConstantEntry FromVersion2String (CsvParser parser) EnumMember = parser.GetField (5), FieldAction = FromFieldActionString (parser.GetField (6)), IsFlags = parser.GetField (7).ToLowerInvariant () == "flags", - DeprecatedSince = parser.GetFieldAsIntNullable (8) + DeprecatedSince = parser.GetFieldAsNullableInt32 (8) }; entry.NormalizeJavaSignature (); diff --git a/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs b/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs index ebd243e05..1fbdb2dd5 100644 --- a/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs +++ b/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs @@ -48,6 +48,20 @@ public static void SaveEnumMapCsv (List constants, string filenam public static void SaveEnumMapCsv (List constants, TextWriter writer) { + var column_names = new [] { + "Action", + "API Level", + "JNI Signature", + "Enum Value", + "C# Enum Type", + "C# Member Name", + "Field Action", + "Is Flags", + "Deprecated Since", + }; + + writer.WriteLine (string.Join (",", column_names)); + foreach (var c in Sort (constants)) writer.WriteLine (c.ToVersion2String ()); } diff --git a/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs b/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs index bfb463ef0..22e43a904 100644 --- a/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs +++ b/src/Java.Interop.Tools.Generator/Utilities/CsvParser.cs @@ -24,7 +24,7 @@ public int GetFieldAsInt (int index) return int.Parse (GetField (index)); } - public int? GetFieldAsIntNullable (int index) + public int? GetFieldAsNullableInt32 (int index) { var value = GetField (index); diff --git a/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs index 029a21c56..8d24860fe 100644 --- a/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs @@ -160,6 +160,33 @@ public void ObsoletedOSPlatformAttributeOverrideSupport () Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Runtime.Versioning.ObsoletedOSPlatform(\"android33.0\")]WithExcluded=1"), writer.ToString ()); } + [Test] + public void ObsoleteAccidentalAddition () + { + var xml = @" + + + + + + + + + "; + + options.UseObsoletedOSPlatformAttributes = true; + + var enu = CreateEnum (); + enu.Value.Members.Single (m => m.EnumMember == "WithExcluded").DeprecatedSince = -1; + + var gens = ParseApiDefinition (xml); + + generator.WriteEnumeration (options, enu, gens.ToArray ()); + + // "-1" is a "magic" API level which adds a message indicating the enum value shouldn't even exist + Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"Thisvaluewasincorrectlyaddedtotheenumerationandisnotavalidvalue\")]WithExcluded=1"), writer.ToString ()); + } + protected new string GetExpected (string testName) { var root = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location); diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs index 2e3f6da98..16a9f6e06 100644 --- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs +++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/EnumGenerator.cs @@ -61,7 +61,8 @@ EnumWriter CreateWriter (CodeGenerationOptions opt, KeyValuePair Date: Thu, 5 Jan 2023 20:00:10 -0600 Subject: [PATCH 3/4] Update EnumMappingFile documentation. --- Documentation/EnumMappingFile.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/EnumMappingFile.md b/Documentation/EnumMappingFile.md index e5143ea38..0b92324bc 100644 --- a/Documentation/EnumMappingFile.md +++ b/Documentation/EnumMappingFile.md @@ -65,7 +65,7 @@ There is now a "v2" version of defining constants. This is a line-level change and you can mix "v1" and "v2" lines in the same file for backwards compatibility, but for consistency it's probably better to stick to one style. -A "v2" line contains up to 8 fields: +A "v2" line contains up to 9 fields: * **Action** - The action to perform. This is what denotes a "v2" line, if the first character is not one of the following it will be treated as "v1". @@ -89,10 +89,14 @@ A "v2" line contains up to 8 fields: * `keep` - Keeps the Java constant * **Flags** - If this field contains `flags` the enum will be created with the `[Flags]` attribute. (Any member will `flags` will make the whole enum `[Flags]`.) - +* **Deprecated Since** - This is generally only used by `Mono.Android.dll` to denote + the Android level the constant was deprecated in. Specifying "-1" will add an obsolete + message to the effect of: "This value was incorrectly added to the enumeration and is + not a valid value". Leave blank if constant is not deprecated. + Full example: ``` -E,10,android/view/Window.PROGRESS_START,0,Android.Views.WindowProgress,Start,remove,flags +E,10,android/view/Window.PROGRESS_START,0,Android.Views.WindowProgress,Start,remove,flags,30 ``` [0]: https://docs.microsoft.com/en-us/xamarin/android/platform/binding-java-library/customizing-bindings/java-bindings-metadata#enumfieldsxml-and-enummethodsxml From c5722426086d5fa2eaa7b86ece8f17a653d78362 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Fri, 6 Jan 2023 09:33:27 -0600 Subject: [PATCH 4/4] Update ConstantsParser.cs --- .../Enumification/ConstantsParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs b/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs index 1fbdb2dd5..94abb9b79 100644 --- a/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs +++ b/src/Java.Interop.Tools.Generator/Enumification/ConstantsParser.cs @@ -60,7 +60,7 @@ public static void SaveEnumMapCsv (List constants, TextWriter wri "Deprecated Since", }; - writer.WriteLine (string.Join (",", column_names)); + writer.WriteLine ("// " + string.Join (",", column_names)); foreach (var c in Sort (constants)) writer.WriteLine (c.ToVersion2String ());