diff --git a/src/Java.Interop.Localization/Resources.Designer.cs b/src/Java.Interop.Localization/Resources.Designer.cs
index 691ab5745..b8c1dc8fe 100644
--- a/src/Java.Interop.Localization/Resources.Designer.cs
+++ b/src/Java.Interop.Localization/Resources.Designer.cs
@@ -19,7 +19,7 @@ namespace Java.Interop.Localization {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
@@ -384,6 +384,15 @@ public static string Generator_BG8A00 {
}
}
+ ///
+ /// Looks up a localized string similar to Invalid namespace transform '{0}'.
+ ///
+ public static string Generator_BG8A07 {
+ get {
+ return ResourceManager.GetString("Generator_BG8A07", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Unknown generic argument constraint type '{0}' for member '{1}'..
///
diff --git a/src/Java.Interop.Localization/Resources.resx b/src/Java.Interop.Localization/Resources.resx
index f49506af4..d441772e5 100644
--- a/src/Java.Interop.Localization/Resources.resx
+++ b/src/Java.Interop.Localization/Resources.resx
@@ -286,6 +286,10 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
{0} - .NET type name
diff --git a/src/Java.Interop.Localization/xlf/Resources.cs.xlf b/src/Java.Interop.Localization/xlf/Resources.cs.xlf
index c2805a256..573b63ea0 100644
--- a/src/Java.Interop.Localization/xlf/Resources.cs.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.cs.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.de.xlf b/src/Java.Interop.Localization/xlf/Resources.de.xlf
index 663a20bca..3bc4cd696 100644
--- a/src/Java.Interop.Localization/xlf/Resources.de.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.de.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.es.xlf b/src/Java.Interop.Localization/xlf/Resources.es.xlf
index 8712a71fa..2f3abf14a 100644
--- a/src/Java.Interop.Localization/xlf/Resources.es.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.es.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.fr.xlf b/src/Java.Interop.Localization/xlf/Resources.fr.xlf
index 854c1f7cb..dbe446898 100644
--- a/src/Java.Interop.Localization/xlf/Resources.fr.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.fr.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.it.xlf b/src/Java.Interop.Localization/xlf/Resources.it.xlf
index 515655eac..2dbdd4112 100644
--- a/src/Java.Interop.Localization/xlf/Resources.it.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.it.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ja.xlf b/src/Java.Interop.Localization/xlf/Resources.ja.xlf
index 8679271f0..c5219f984 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ja.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ja.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ko.xlf b/src/Java.Interop.Localization/xlf/Resources.ko.xlf
index 818ed84a1..de12f99b9 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ko.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ko.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.pl.xlf b/src/Java.Interop.Localization/xlf/Resources.pl.xlf
index 7a2f58e5e..9ab39c8f1 100644
--- a/src/Java.Interop.Localization/xlf/Resources.pl.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.pl.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf b/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
index 1d09ad5c0..8df0f8d90 100644
--- a/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.pt-BR.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.ru.xlf b/src/Java.Interop.Localization/xlf/Resources.ru.xlf
index e5ea1ca7d..69192c753 100644
--- a/src/Java.Interop.Localization/xlf/Resources.ru.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.ru.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.tr.xlf b/src/Java.Interop.Localization/xlf/Resources.tr.xlf
index f6a4eedee..36225df9d 100644
--- a/src/Java.Interop.Localization/xlf/Resources.tr.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.tr.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf b/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
index 429a4b640..21db2e011 100644
--- a/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.zh-Hans.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf b/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
index 1ba4eaa3c..01dbdf7f2 100644
--- a/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
+++ b/src/Java.Interop.Localization/xlf/Resources.zh-Hant.xlf
@@ -207,6 +207,11 @@ The following terms should not be translated: <package>.
{0} - XML transform. (example: '<remove-node path="/api/package[@name='javax.sql']"')
The following terms should not be translated: Metadata.xml.
+
+ Invalid namespace transform '{0}'
+ Invalid namespace transform '{0}'
+ {0} - XML transform. (example: '<ns-replace source="example" replacement="Example" />')
+
Unknown generic argument constraint type '{0}' for member '{1}'.
Unknown generic argument constraint type '{0}' for member '{1}'.
diff --git a/src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs b/src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs
index 935885d5d..a20b2833f 100644
--- a/src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs
+++ b/src/Java.Interop.Tools.Generator/Extensions/UtilityExtensions.cs
@@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Text;
using System.Xml;
using System.Xml.Linq;
@@ -41,5 +43,33 @@ public static bool StartsWithAny (this string value, params string [] values)
return null;
}
+
+ // A case-insensitive Replace doesn't exist in classic .NET Framework. Loosely based on:
+ // https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
+ public static string ReplaceOrdinalIgnoreCase (this string source, string oldValue, string newValue)
+ {
+ var result = new StringBuilder ();
+ var pos = 0;
+
+ while (true) {
+ var index = source.IndexOf (oldValue, pos, StringComparison.OrdinalIgnoreCase);
+
+ // Not found, bail
+ if (index < 0)
+ break;
+
+ // Append the unmodified portion of search space
+ result.Append (source.Substring (pos, index));
+
+ // Append the replacement
+ result.Append (newValue);
+
+ pos = index + oldValue.Length;
+ }
+
+ // Append what remains of the search space, then allocate the new string.
+ result.Append (source.Substring (pos));
+ return result.ToString ();
+ }
}
}
diff --git a/src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs b/src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs
index 96b4bcfa4..50e459ebe 100644
--- a/src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs
+++ b/src/Java.Interop.Tools.Generator/Metadata/FixupXmlDocument.cs
@@ -4,6 +4,7 @@
using System.Xml.Linq;
using Xamarin.Android.Tools;
+using System.Collections.Generic;
namespace Java.Interop.Tools.Generator
{
@@ -168,6 +169,18 @@ public void Apply (ApiXmlDocument apiDocument, string apiLevelString, int produc
}
}
+ public IList GetNamespaceTransforms ()
+ {
+ var list = new List ();
+
+ foreach (var xe in FixupDocument.XPathSelectElements ("/metadata/ns-replace")) {
+ if (NamespaceTransform.TryParse (xe, out var transform))
+ list.Add (transform);
+ }
+
+ return list;
+ }
+
bool ShouldSkip (XElement node, int apiLevel, int productVersion)
{
if (apiLevel > 0) {
diff --git a/src/Java.Interop.Tools.Generator/Metadata/NamespaceTransform.cs b/src/Java.Interop.Tools.Generator/Metadata/NamespaceTransform.cs
new file mode 100644
index 000000000..4db92796e
--- /dev/null
+++ b/src/Java.Interop.Tools.Generator/Metadata/NamespaceTransform.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Xml.Linq;
+using Xamarin.Android.Tools;
+
+namespace Java.Interop.Tools.Generator
+{
+ public class NamespaceTransform
+ {
+ public string OldValue { get; }
+ public string NewValue { get; }
+ public bool IsStartsWith { get; }
+ public bool IsEndsWith { get; }
+
+ public NamespaceTransform (string oldValue, string newValue)
+ {
+ OldValue = oldValue;
+ NewValue = newValue;
+
+ if (OldValue.EndsWith (".", StringComparison.Ordinal)) {
+ IsStartsWith = true;
+ OldValue = OldValue.Substring (0, OldValue.Length - 1);
+ }
+
+ if (OldValue.StartsWith (".", StringComparison.Ordinal)) {
+ IsEndsWith = true;
+ OldValue = OldValue.Substring (1);
+ }
+ }
+
+ public string ApplyInternal (string value)
+ {
+ string result;
+
+ while (true) {
+ result = ApplyInternal (value);
+
+ if (result == value)
+ return result;
+
+ value = result;
+ }
+ }
+
+ public string Apply (string value)
+ {
+ // Handle a "starts with" and "ends with" transform
+ if (IsStartsWith && IsEndsWith) {
+ if (value.Equals (OldValue, StringComparison.OrdinalIgnoreCase))
+ return NewValue;
+
+ // Don't let this fall through
+ return value;
+ }
+
+ // Handle a "starts with" transform
+ if (IsStartsWith) {
+ if (value.StartsWith (OldValue, StringComparison.OrdinalIgnoreCase))
+ return (NewValue + value.Substring (OldValue.Length)).TrimStart ('.');
+
+ return value;
+ }
+
+ // Handle an "ends with" transform
+ if (IsEndsWith) {
+ if (value.EndsWith (OldValue, StringComparison.OrdinalIgnoreCase))
+ return (value.Substring (0, value.Length - OldValue.Length) + NewValue).TrimEnd ('.');
+
+ return value;
+ }
+
+ // Handle an "anywhere" transform
+ var value_tokens = value.Split ('.');
+ var match_tokens = OldValue.Split ('.');
+
+ var results = new List ();
+
+ for (var i = 0; i < value_tokens.Length; i++) {
+ if (AtMatch (value_tokens, i, match_tokens, 0)) {
+ if (NewValue.HasValue ())
+ results.Add (NewValue);
+
+ i += match_tokens.Length - 1;
+ } else {
+ results.Add (value_tokens [i]);
+ }
+ }
+
+ return string.Join (".", results);
+ }
+
+ public static bool TryParse (XElement element, [NotNullWhen (true)] out NamespaceTransform? transform)
+ {
+ var source = element.XGetAttribute ("source");
+ var replacement = element.XGetAttribute ("replacement");
+
+ if (!source.HasValue () || replacement is null) {
+ Report.LogCodedWarning (0, Report.WarningInvalidNamespaceTransform, null, element, element.ToString ());
+ transform = null;
+ return false;
+ }
+
+ transform = new NamespaceTransform (source, replacement);
+ return true;
+ }
+
+ private bool AtMatch (string [] valueTokens, int valueIndex, string [] matchTokens, int matchIndex)
+ {
+ if (matchIndex >= matchTokens.Length)
+ return true;
+
+ if (valueIndex >= valueTokens.Length)
+ return false;
+
+ if (string.Compare (valueTokens [valueIndex], matchTokens [matchIndex], StringComparison.OrdinalIgnoreCase) == 0)
+ return AtMatch (valueTokens, valueIndex + 1, matchTokens, matchIndex + 1);
+
+ return false;
+ }
+ }
+}
+
diff --git a/src/Java.Interop.Tools.Generator/Utilities/Report.cs b/src/Java.Interop.Tools.Generator/Utilities/Report.cs
index aa81371ba..a556a3d93 100644
--- a/src/Java.Interop.Tools.Generator/Utilities/Report.cs
+++ b/src/Java.Interop.Tools.Generator/Utilities/Report.cs
@@ -67,6 +67,7 @@ public LocalizedMessage (int code, string value)
public static LocalizedMessage WarningAttrMatchedNoNodes => new LocalizedMessage (0x8A04, Localization.Resources.Generator_BG8A00);
public static LocalizedMessage WarningMoveNodeMatchedNoNodes => new LocalizedMessage (0x8A05, Localization.Resources.Generator_BG8A00);
public static LocalizedMessage WarningRemoveAttrMatchedNoNodes => new LocalizedMessage (0x8A06, Localization.Resources.Generator_BG8A00);
+ public static LocalizedMessage WarningInvalidNamespaceTransform => new LocalizedMessage (0x8A07, Localization.Resources.Generator_BG8A07);
public static LocalizedMessage WarningUnknownGenericConstraint => new LocalizedMessage (0x8B00, Localization.Resources.Generator_BG8B00);
public static LocalizedMessage WarningBaseInterfaceNotFound => new LocalizedMessage (0x8C00, Localization.Resources.Generator_BG8C00);
public static LocalizedMessage WarningBaseInterfaceInvalid => new LocalizedMessage (0x8C01, Localization.Resources.Generator_BG8C01);
diff --git a/tests/Java.Interop.Tools.Generator-Tests/Metadata/NamespaceTransformTests.cs b/tests/Java.Interop.Tools.Generator-Tests/Metadata/NamespaceTransformTests.cs
new file mode 100644
index 000000000..7da4b2cd0
--- /dev/null
+++ b/tests/Java.Interop.Tools.Generator-Tests/Metadata/NamespaceTransformTests.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Xml.Linq;
+using Java.Interop.Tools.Generator;
+using NUnit.Framework;
+
+namespace Java.Interop.Tools.Generator_Tests
+{
+ public class NamespaceTransformTests
+ {
+ [Test]
+ public void ParseNamespaceTransform ()
+ {
+ var doc = XDocument.Parse ("");
+ var result = NamespaceTransform.TryParse (doc.Root.Element ("ns-replace"), out var nt);
+
+ Assert.IsTrue (result);
+ Assert.IsNotNull (nt);
+ Assert.AreEqual ("com.example", nt.OldValue);
+ Assert.AreEqual ("Xamarin", nt.NewValue);
+ }
+
+ [Test]
+ public void ParseNamespaceTransform2 ()
+ {
+ var doc = XDocument.Parse ("");
+ var result = NamespaceTransform.TryParse (doc.Root.Element ("ns-replace"), out var nt);
+
+ Assert.IsTrue (result);
+ Assert.IsNotNull (nt);
+ Assert.AreEqual ("com.example", nt.OldValue);
+ Assert.AreEqual ("", nt.NewValue);
+ }
+
+ [Test]
+ public void ParseInvalidNamespaceTransform ()
+ {
+ // Logs: warning BG8A07: Invalid namespace transform ''
+ var doc = XDocument.Parse ("");
+ var result = NamespaceTransform.TryParse (doc.Root.Element ("ns-replace"), out var nt);
+
+ Assert.IsFalse (result);
+ Assert.IsNull (nt);
+ }
+
+ [Test]
+ public void ParseInvalidNamespaceTransform2 ()
+ {
+ // Logs: warning BG8A07: Invalid namespace transform ''
+ var doc = XDocument.Parse ("");
+ var result = NamespaceTransform.TryParse (doc.Root.Element ("ns-replace"), out var nt);
+
+ Assert.IsFalse (result);
+ Assert.IsNull (nt);
+ }
+
+ [Test]
+ public void GetTransformedNamespace ()
+ {
+ // Normal and case-insensitive
+ AssertTransformedNamespace ("Androidx.Core", "AndroidX.Core", new NamespaceTransform ("Androidx", "AndroidX"));
+ AssertTransformedNamespace ("Androidx.Core", "AndroidX.Core", new NamespaceTransform ("androidx", "AndroidX"));
+
+ // Replace 1 level with 2
+ AssertTransformedNamespace ("Androidx.Core", "Xamarin.AndroidX.Core", new NamespaceTransform ("androidx", "Xamarin.AndroidX"));
+
+ // Replace 2 levels with 1
+ AssertTransformedNamespace ("Google.Androidx.Core", "AndroidX.Core", new NamespaceTransform ("Google.Androidx", "AndroidX"));
+
+ // Replace 2 levels with 2
+ AssertTransformedNamespace ("Google.Androidx.Core", "Xamarin.AndroidX.Core", new NamespaceTransform ("Google.Androidx", "Xamarin.AndroidX"));
+
+ // Removing a match
+ AssertTransformedNamespace ("Androidx.Core.Test", "Androidx.Test", new NamespaceTransform ("core", ""));
+ AssertTransformedNamespace ("Androidx.Core.Test", "Androidx.Core", new NamespaceTransform ("test", ""));
+ AssertTransformedNamespace ("Androidx.Core.Test", "Core.Test", new NamespaceTransform ("androidx", ""));
+
+ // Multiple matches
+ AssertTransformedNamespace ("Androidx.Androidx.Core", "AndroidX.AndroidX.Core", new NamespaceTransform ("androidx", "AndroidX"));
+ AssertTransformedNamespace ("google.androidx.Core", "Xamarin.AndroidX.Core", new NamespaceTransform ("androidx", "AndroidX"), new NamespaceTransform ("google", "Xamarin"));
+
+ // Starts with and ends with
+ AssertTransformedNamespace ("example", "Transformed", new NamespaceTransform (".example.", "Transformed"));
+ AssertTransformedNamespace ("Androidx.Core", "Transformed", new NamespaceTransform (".Androidx.Core.", "Transformed"));
+ AssertTransformedNamespace ("Androidx.Core", "Androidx.Core", new NamespaceTransform (".Core.", "Transformed"));
+ AssertTransformedNamespace ("Androidx.Core", "Androidx.Core", new NamespaceTransform (".AndroidX.", "Transformed"));
+
+ // Starts with
+ AssertTransformedNamespace ("Androidx.Androidx.Core", "AndroidX2.Androidx.Core", new NamespaceTransform ("AndroidX.", "AndroidX2"));
+ AssertTransformedNamespace ("Androidx.Androidx.Core", "Androidx.Core", new NamespaceTransform ("AndroidX.", ""));
+
+ // Ends with
+ AssertTransformedNamespace ("Androidx.Core.Core", "Androidx.Core.Core2", new NamespaceTransform (".core", "Core2"));
+ AssertTransformedNamespace ("Androidx.Core.Core", "Androidx.Core", new NamespaceTransform (".core", ""));
+
+ // Only matches full level
+ AssertTransformedNamespace ("AndroidX.Test.Tests", "AndroidX.NewTest.Tests", new NamespaceTransform ("Test", "NewTest"));
+ }
+
+ void AssertTransformedNamespace (string value, string expected, params NamespaceTransform [] transforms)
+ {
+ foreach (var nt in transforms)
+ value = nt.Apply (value);
+
+ Assert.AreEqual (expected, value);
+ }
+ }
+}
diff --git a/tests/generator-Tests/Integration-Tests/BaseGeneratorTest.cs b/tests/generator-Tests/Integration-Tests/BaseGeneratorTest.cs
index d78b9113b..55c051cc6 100644
--- a/tests/generator-Tests/Integration-Tests/BaseGeneratorTest.cs
+++ b/tests/generator-Tests/Integration-Tests/BaseGeneratorTest.cs
@@ -121,12 +121,12 @@ private byte[] ReadAllBytesIgnoringLineEndings (string path)
}
}
- protected void RunAllTargets (string outputRelativePath, string apiDescriptionFile, string expectedRelativePath, string[] additionalSupportPaths = null, string enumFieldsMapFile = null, string enumMethodMapFile = null)
+ protected void RunAllTargets (string outputRelativePath, string apiDescriptionFile, string expectedRelativePath, string[] additionalSupportPaths = null, string enumFieldsMapFile = null, string enumMethodMapFile = null, string metadataFile = null)
{
- Run (CodeGenerationTarget.XamarinAndroid, Path.Combine ("out", outputRelativePath), apiDescriptionFile, Path.Combine ("expected", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile);
- Run (CodeGenerationTarget.XAJavaInterop1, Path.Combine ("out.xaji", outputRelativePath), apiDescriptionFile, Path.Combine ("expected.xaji", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile);
+ Run (CodeGenerationTarget.XamarinAndroid, Path.Combine ("out", outputRelativePath), apiDescriptionFile, Path.Combine ("expected", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile, metadataFile);
+ Run (CodeGenerationTarget.XAJavaInterop1, Path.Combine ("out.xaji", outputRelativePath), apiDescriptionFile, Path.Combine ("expected.xaji", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile, metadataFile);
if (TryJavaInterop1) {
- Run (CodeGenerationTarget.JavaInterop1, Path.Combine ("out.ji", outputRelativePath), apiDescriptionFile, Path.Combine ("expected.ji", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile);
+ Run (CodeGenerationTarget.JavaInterop1, Path.Combine ("out.ji", outputRelativePath), apiDescriptionFile, Path.Combine ("expected.ji", expectedRelativePath), additionalSupportPaths, enumFieldsMapFile, enumMethodMapFile, metadataFile);
}
}
@@ -136,7 +136,7 @@ protected string FullPath (string path)
return Path.Combine (dir, path.Replace ('/', Path.DirectorySeparatorChar));
}
- protected void Run (CodeGenerationTarget target, string outputPath, string apiDescriptionFile, string expectedPath, string[] additionalSupportPaths = null, string enumFieldsMapFile = null, string enumMethodMapFile = null)
+ protected void Run (CodeGenerationTarget target, string outputPath, string apiDescriptionFile, string expectedPath, string[] additionalSupportPaths = null, string enumFieldsMapFile = null, string enumMethodMapFile = null, string metadataFile = null)
{
Cleanup (outputPath);
AdditionalSourceDirectories.Clear ();
@@ -151,6 +151,9 @@ protected void Run (CodeGenerationTarget target, string outputPath, string apiDe
if (!string.IsNullOrWhiteSpace (enumMethodMapFile))
Options.EnumMethodsMapFile = FullPath (enumMethodMapFile);
+ if (!string.IsNullOrWhiteSpace (metadataFile))
+ Options.FixupFiles.Add (metadataFile);
+
var adjuster_output = Path.Combine (Path.GetTempPath (), "generator-tests");
Directory.CreateDirectory (adjuster_output);
diff --git a/tests/generator-Tests/Integration-Tests/Core_ClassParse.cs b/tests/generator-Tests/Integration-Tests/Core_ClassParse.cs
index 2a710c4ee..3f4b9af17 100644
--- a/tests/generator-Tests/Integration-Tests/Core_ClassParse.cs
+++ b/tests/generator-Tests/Integration-Tests/Core_ClassParse.cs
@@ -14,7 +14,8 @@ public void GeneratedOK ()
RunAllTargets (
outputRelativePath: "Core_ClassParse",
apiDescriptionFile: "expected/Core_ClassParse/api.xml",
- expectedRelativePath: "Core_ClassParse");
+ expectedRelativePath: "Core_ClassParse",
+ metadataFile: FullPath ("expected/Core_ClassParse/metadata.xml"));
}
}
}
diff --git a/tests/generator-Tests/Unit-Tests/FixupXmlDocumentTests.cs b/tests/generator-Tests/Unit-Tests/FixupXmlDocumentTests.cs
index 5ce1c72e1..42e849c1d 100644
--- a/tests/generator-Tests/Unit-Tests/FixupXmlDocumentTests.cs
+++ b/tests/generator-Tests/Unit-Tests/FixupXmlDocumentTests.cs
@@ -85,6 +85,19 @@ public void RemoveAttribute ()
Assert.AreEqual ("", api.ApiDocument.ToString (SaveOptions.DisableFormatting).Replace ('\"', '\''));
}
+ [Test]
+ public void ParseNamespaceTransforms ()
+ {
+ var fixup = GetFixupXmlDocument ("");
+ var transforms = fixup.GetNamespaceTransforms ();
+
+ Assert.AreEqual (2, transforms.Count);
+ Assert.AreEqual ("androidx", transforms [0].OldValue);
+ Assert.AreEqual ("AndroidX", transforms [0].NewValue);
+ Assert.AreEqual ("com.google", transforms [1].OldValue);
+ Assert.AreEqual ("Xamarin", transforms [1].NewValue);
+ }
+
ApiXmlDocument GetXmlApiDocument ()
{
var api = "";
diff --git a/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs b/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs
index 9405852d1..184f37c64 100644
--- a/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs
+++ b/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
+using Java.Interop.Tools.Generator;
using MonoDroid.Generation;
using NUnit.Framework;
@@ -302,5 +303,25 @@ public void IgnoreUserObfuscatedTypes ()
// None of these should be parsed because the user has said they are obfuscated
Assert.AreEqual (0, gens.Count);
}
+
+ [Test]
+ public void TransformNamespaces ()
+ {
+ var xml = XDocument.Parse (@"
+
+
+
+
+ ");
+
+ var opt = new CodeGenerationOptions ();
+ opt.NamespaceTransforms.Add (new NamespaceTransform ("com.example", "Example"));
+ opt.NamespaceTransforms.Add (new NamespaceTransform (".test", "Tests"));
+
+ var gens = XmlApiImporter.Parse (xml, opt);
+
+ Assert.AreEqual (1, gens.Count);
+ Assert.AreEqual ("Example.Tests", gens [0].Namespace);
+ }
}
}
diff --git a/tests/generator-Tests/expected.ji/Core_ClassParse/Mono.Android.projitems b/tests/generator-Tests/expected.ji/Core_ClassParse/Mono.Android.projitems
index e94c83f04..ea168730b 100644
--- a/tests/generator-Tests/expected.ji/Core_ClassParse/Mono.Android.projitems
+++ b/tests/generator-Tests/expected.ji/Core_ClassParse/Mono.Android.projitems
@@ -7,6 +7,7 @@
+
diff --git a/tests/generator-Tests/expected.ji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs b/tests/generator-Tests/expected.ji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
new file mode 100644
index 000000000..06d93f40b
--- /dev/null
+++ b/tests/generator-Tests/expected.ji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using Java.Interop;
+
+namespace Xamarin.Google.Composable {
+
+ // Metadata.xml XPath class reference: path="/api/package[@name='com.com.google.compose']/class[@name='MyClass']"
+ [global::Java.Interop.JniTypeSignature ("com/com/google/compose/MyClass", GenerateJavaPeer=false)]
+ public partial class MyClass : global::Java.Lang.Object {
+ static readonly JniPeerMembers _members = new JniPeerMembers ("com/com/google/compose/MyClass", typeof (MyClass));
+
+ [global::System.Diagnostics.DebuggerBrowsable (global::System.Diagnostics.DebuggerBrowsableState.Never)]
+ [global::System.ComponentModel.EditorBrowsable (global::System.ComponentModel.EditorBrowsableState.Never)]
+ public override global::Java.Interop.JniPeerMembers JniPeerMembers {
+ get { return _members; }
+ }
+
+ protected MyClass (ref JniObjectReference reference, JniObjectReferenceOptions options) : base (ref reference, options)
+ {
+ }
+
+ }
+}
diff --git a/tests/generator-Tests/expected.xaji/Core_ClassParse/Java.Interop.__TypeRegistrations.cs b/tests/generator-Tests/expected.xaji/Core_ClassParse/Java.Interop.__TypeRegistrations.cs
index 05fa69b48..2e45df39d 100644
--- a/tests/generator-Tests/expected.xaji/Core_ClassParse/Java.Interop.__TypeRegistrations.cs
+++ b/tests/generator-Tests/expected.xaji/Core_ClassParse/Java.Interop.__TypeRegistrations.cs
@@ -14,9 +14,11 @@ public static void RegisterPackages ()
#endif // def MONODROID_TIMING
Java.Interop.TypeManager.RegisterPackages (
new string[]{
+ "com/com/google/compose",
"xamarin/test/invalidnames",
},
new Converter[]{
+ lookup_com_com_google_compose_package,
lookup_xamarin_test_invalidnames_package,
});
#if MONODROID_TIMING
@@ -36,6 +38,18 @@ static Type Lookup (string[] mappings, string javaType)
return Type.GetType (managedType);
}
+ static string[] package_com_com_google_compose_mappings;
+ static Type lookup_com_com_google_compose_package (string klass)
+ {
+ if (package_com_com_google_compose_mappings == null) {
+ package_com_com_google_compose_mappings = new string[]{
+ "com/com/google/compose/MyClass:Xamarin.Google.Composable.MyClass",
+ };
+ }
+
+ return Lookup (package_com_com_google_compose_mappings, klass);
+ }
+
static string[] package_xamarin_test_invalidnames_mappings;
static Type lookup_xamarin_test_invalidnames_package (string klass)
{
diff --git a/tests/generator-Tests/expected.xaji/Core_ClassParse/Mono.Android.projitems b/tests/generator-Tests/expected.xaji/Core_ClassParse/Mono.Android.projitems
index c6fa7bb59..5a1d5a97e 100644
--- a/tests/generator-Tests/expected.xaji/Core_ClassParse/Mono.Android.projitems
+++ b/tests/generator-Tests/expected.xaji/Core_ClassParse/Mono.Android.projitems
@@ -8,6 +8,7 @@
+
diff --git a/tests/generator-Tests/expected.xaji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs b/tests/generator-Tests/expected.xaji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
new file mode 100644
index 000000000..a1288ce7e
--- /dev/null
+++ b/tests/generator-Tests/expected.xaji/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using Android.Runtime;
+using Java.Interop;
+
+namespace Xamarin.Google.Composable {
+
+ // Metadata.xml XPath class reference: path="/api/package[@name='com.com.google.compose']/class[@name='MyClass']"
+ [global::Android.Runtime.Register ("com/com/google/compose/MyClass", DoNotGenerateAcw=true)]
+ public partial class MyClass : global::Java.Lang.Object {
+ static readonly JniPeerMembers _members = new XAPeerMembers ("com/com/google/compose/MyClass", typeof (MyClass));
+
+ internal static new IntPtr class_ref {
+ get { return _members.JniPeerType.PeerReference.Handle; }
+ }
+
+ [global::System.Diagnostics.DebuggerBrowsable (global::System.Diagnostics.DebuggerBrowsableState.Never)]
+ [global::System.ComponentModel.EditorBrowsable (global::System.ComponentModel.EditorBrowsableState.Never)]
+ public override global::Java.Interop.JniPeerMembers JniPeerMembers {
+ get { return _members; }
+ }
+
+ [global::System.Diagnostics.DebuggerBrowsable (global::System.Diagnostics.DebuggerBrowsableState.Never)]
+ [global::System.ComponentModel.EditorBrowsable (global::System.ComponentModel.EditorBrowsableState.Never)]
+ protected override IntPtr ThresholdClass {
+ get { return _members.JniPeerType.PeerReference.Handle; }
+ }
+
+ [global::System.Diagnostics.DebuggerBrowsable (global::System.Diagnostics.DebuggerBrowsableState.Never)]
+ [global::System.ComponentModel.EditorBrowsable (global::System.ComponentModel.EditorBrowsableState.Never)]
+ protected override global::System.Type ThresholdType {
+ get { return _members.ManagedPeerType; }
+ }
+
+ protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer)
+ {
+ }
+
+ }
+}
diff --git a/tests/generator-Tests/expected.xaji/Core_ClassParse/__NamespaceMapping__.cs b/tests/generator-Tests/expected.xaji/Core_ClassParse/__NamespaceMapping__.cs
index 966d718be..4ebe1a2cc 100644
--- a/tests/generator-Tests/expected.xaji/Core_ClassParse/__NamespaceMapping__.cs
+++ b/tests/generator-Tests/expected.xaji/Core_ClassParse/__NamespaceMapping__.cs
@@ -2,6 +2,7 @@
[assembly:global::Android.Runtime.NamespaceMapping (Java = "java.lang", Managed="Java.Lang")]
[assembly:global::Android.Runtime.NamespaceMapping (Java = "xamarin.test.invalidnames", Managed="Xamarin.Test.Invalidnames")]
+[assembly:global::Android.Runtime.NamespaceMapping (Java = "com.com.google.compose", Managed="Xamarin.Google.Composable")]
#if !NET
namespace System.Runtime.Versioning {
diff --git a/tests/generator-Tests/expected/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs b/tests/generator-Tests/expected/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
new file mode 100644
index 000000000..998184788
--- /dev/null
+++ b/tests/generator-Tests/expected/Core_ClassParse/Xamarin.Google.Composable.MyClass.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using Android.Runtime;
+
+namespace Xamarin.Google.Composable {
+
+ // Metadata.xml XPath class reference: path="/api/package[@name='com.com.google.compose']/class[@name='MyClass']"
+ [global::Android.Runtime.Register ("com/com/google/compose/MyClass", DoNotGenerateAcw=true)]
+ public partial class MyClass : global::Java.Lang.Object {
+
+ internal static new IntPtr java_class_handle;
+ internal static new IntPtr class_ref {
+ get {
+ return JNIEnv.FindClass ("com/com/google/compose/MyClass", ref java_class_handle);
+ }
+ }
+
+ protected override IntPtr ThresholdClass {
+ get { return class_ref; }
+ }
+
+ protected override global::System.Type ThresholdType {
+ get { return typeof (MyClass); }
+ }
+
+ protected MyClass (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {}
+
+ }
+}
diff --git a/tests/generator-Tests/expected/Core_ClassParse/api.xml b/tests/generator-Tests/expected/Core_ClassParse/api.xml
index 41015a890..6289ee402 100644
--- a/tests/generator-Tests/expected/Core_ClassParse/api.xml
+++ b/tests/generator-Tests/expected/Core_ClassParse/api.xml
@@ -22,5 +22,8 @@
+
+
+
diff --git a/tests/generator-Tests/expected/Core_ClassParse/metadata.xml b/tests/generator-Tests/expected/Core_ClassParse/metadata.xml
new file mode 100644
index 000000000..fa358dfa5
--- /dev/null
+++ b/tests/generator-Tests/expected/Core_ClassParse/metadata.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/generator/CodeGenerationOptions.cs b/tools/generator/CodeGenerationOptions.cs
index ced29d57b..cf66e1138 100644
--- a/tools/generator/CodeGenerationOptions.cs
+++ b/tools/generator/CodeGenerationOptions.cs
@@ -80,6 +80,8 @@ public bool BuildingCoreAssembly {
public string NullForgivingOperator => SupportNullableReferenceTypes ? "!" : string.Empty;
+ public List NamespaceTransforms { get; } = new List ();
+
public string GetTypeReferenceName (Field field)
{
var name = GetOutputName (field.Symbol.FullName);
@@ -282,6 +284,17 @@ public string GetFileName (string fullName)
return s;
}
}
+
+ public string GetTransformedNamespace (string value)
+ {
+ if (!value.HasValue () || !NamespaceTransforms.Any ())
+ return value;
+
+ foreach (var nt in NamespaceTransforms)
+ value = nt.Apply (value);
+
+ return value;
+ }
}
}
diff --git a/tools/generator/CodeGenerator.cs b/tools/generator/CodeGenerator.cs
index d246a952e..c7271d261 100644
--- a/tools/generator/CodeGenerator.cs
+++ b/tools/generator/CodeGenerator.cs
@@ -176,8 +176,12 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
return;
// Apply metadata fixups
- foreach (var fixup in fixups)
- api.ApplyFixupFile (fixup);
+ foreach (var fixup in fixups) {
+ if (FixupXmlDocument.Load (fixup) is FixupXmlDocument f) {
+ api.ApplyFixupFile (f);
+ opt.NamespaceTransforms.AddRange (f.GetNamespaceTransforms ());
+ }
+ }
api.ApiDocument.Save (apiXmlFile + ".fixed");
diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs
index 630a923e9..3c80a0c25 100644
--- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs
+++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs
@@ -105,7 +105,7 @@ public static List ParsePackage (XElement ns, CodeGenerationOptions opt
public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationOptions options)
{
- var klass = new ClassGen (CreateGenBaseSupport (pkg, elem, false)) {
+ var klass = new ClassGen (CreateGenBaseSupport (pkg, elem, options, false)) {
BaseType = elem.XGetAttribute ("extends"),
FromXml = true,
IsAbstract = elem.XGetAttribute ("abstract") == "true",
@@ -234,7 +234,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener
return field;
}
- public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, bool isInterface)
+ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, CodeGenerationOptions opt, bool isInterface)
{
var support = new GenBaseSupport {
IsAcw = true,
@@ -258,7 +258,7 @@ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem,
if (pkg.Attribute ("managedName") != null)
support.Namespace = pkg.XGetAttribute ("managedName");
else
- support.Namespace = StringRocks.PackageToPascalCase (support.PackageName);
+ support.Namespace = opt.GetTransformedNamespace (StringRocks.PackageToPascalCase (support.PackageName));
var tpn = elem.Element ("typeParameters");
@@ -302,7 +302,7 @@ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem,
public static InterfaceGen CreateInterface (XElement pkg, XElement elem, CodeGenerationOptions options)
{
- var iface = new InterfaceGen (CreateGenBaseSupport (pkg, elem, true)) {
+ var iface = new InterfaceGen (CreateGenBaseSupport (pkg, elem, options, true)) {
ArgsType = elem.XGetAttribute ("argsType"),
HasManagedName = elem.Attribute ("managedName") != null,
NoAlternatives = elem.XGetAttribute ("no-alternatives") == "true",