diff --git a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs index 1f355c722..f8b7c7005 100644 --- a/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs +++ b/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs @@ -522,8 +522,13 @@ public void WriteInterfaceDeclaration (InterfaceGen @interface, string indent) if (@interface.IsDeprecated) writer.WriteLine ("{0}[ObsoleteAttribute (@\"{1}\")]", indent, @interface.DeprecatedComment); - if (!@interface.IsConstSugar) - writer.WriteLine ("{0}[Register (\"{1}\", \"\", \"{2}\"{3})]", indent, @interface.RawJniName, @interface.Namespace + "." + @interface.FullName.Substring (@interface.Namespace.Length + 1).Replace ('.', '/') + "Invoker", @interface.AdditionalAttributeString ()); + if (!@interface.IsConstSugar) { + var signature = string.IsNullOrWhiteSpace (@interface.Namespace) + ? @interface.FullName.Replace ('.', '/') + : @interface.Namespace + "." + @interface.FullName.Substring (@interface.Namespace.Length + 1).Replace ('.', '/'); + + writer.WriteLine ("{0}[Register (\"{1}\", \"\", \"{2}\"{3})]", indent, @interface.RawJniName, signature + "Invoker", @interface.AdditionalAttributeString ()); + } if (@interface.TypeParameters != null && @interface.TypeParameters.Any ()) writer.WriteLine ("{0}{1}", indent, @interface.TypeParameters.ToGeneratedAttributeString ()); diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index 2cfe6097d..7e4609645 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -131,7 +131,7 @@ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, PackageName = pkg.XGetAttribute ("name"), Visibility = elem.XGetAttribute ("visibility") }; - + if (support.IsDeprecated) { support.DeprecatedComment = elem.XGetAttribute ("deprecated"); @@ -165,7 +165,7 @@ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, if (elem.Attribute ("managedName") != null) { support.Name = elem.XGetAttribute ("managedName"); - support.FullName = string.Format ("{0}.{1}", support.Namespace, support.Name); + support.FullName = string.IsNullOrWhiteSpace(support.Namespace) ? support.Name : $"{support.Namespace}.{support.Name}"; int idx = support.Name.LastIndexOf ('.'); support.Name = idx > 0 ? support.Name.Substring (idx + 1) : support.Name; raw_name = support.Name; @@ -177,7 +177,9 @@ public static GenBaseSupport CreateGenBaseSupport (XElement pkg, XElement elem, raw_name = support.Name; support.TypeNamePrefix = isInterface ? IsPrefixableName (raw_name) ? "I" : string.Empty : string.Empty; support.Name = EnsureValidIdentifer (support.TypeNamePrefix + raw_name); - support.FullName = string.Format ("{0}.{1}{2}", support.Namespace, idx > 0 ? StringRocks.TypeToPascalCase (support.JavaSimpleName.Substring (0, idx + 1)) : string.Empty, support.Name); + var supportNamespace = string.IsNullOrWhiteSpace (support.Namespace) ? string.Empty : $"{support.Namespace}."; + var supportSimpleName = idx > 0 ? StringRocks.TypeToPascalCase (support.JavaSimpleName.Substring (0, idx + 1)) : string.Empty; + support.FullName = string.Format ("{0}{1}{2}", supportNamespace, supportSimpleName, support.Name); } support.IsObfuscated = IsObfuscatedName (pkg.Elements ().Count (), support.JavaSimpleName) && elem.XGetAttribute ("obfuscated") != "false"; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs index fc1c84ea5..914d48595 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs @@ -137,13 +137,18 @@ public override void Generate (CodeGenerationOptions opt, GenerationInfo gen_inf sw.WriteLine ("using Java.Interop;"); } sw.WriteLine (); - sw.WriteLine ("namespace {0} {{", Namespace); - sw.WriteLine (); + var hasNamespace = !string.IsNullOrWhiteSpace (Namespace); + if (hasNamespace) { + sw.WriteLine ("namespace {0} {{", Namespace); + sw.WriteLine (); + } var generator = opt.CreateCodeGenerator (sw); - generator.WriteClass (this, "\t", gen_info); + generator.WriteClass (this, hasNamespace ? "\t" : string.Empty, gen_info); - sw.WriteLine ("}"); + if (hasNamespace) { + sw.WriteLine ("}"); + } } } diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs index 3e871f0f3..ec22211c1 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs @@ -170,8 +170,9 @@ IEnumerable Ancestors () // not: not currently assembly qualified, but it uses needed // Type.GetType() conventions such as '/' for nested types. - public string AssemblyQualifiedName => - $"{Namespace}.{FullName.Substring (Namespace.Length + 1).Replace ('.', '/')}"; + public string AssemblyQualifiedName => string.IsNullOrWhiteSpace (Namespace) + ? $"{FullName.Replace ('.', '/')}" + : $"{Namespace}." + $"{FullName.Substring (Namespace.Length + 1).Replace ('.', '/')}"; public int ApiAvailableSince { get; set; } @@ -590,7 +591,9 @@ static bool IsTypeCommensurate (CodeGenerationOptions opt, ISymbol sym) public bool IsValid { get; set; } = true; - public string JavaName => $"{PackageName}.{JavaSimpleName}"; + public string JavaName => string.IsNullOrWhiteSpace (PackageName) + ? JavaSimpleName + : $"{PackageName}.{JavaSimpleName}"; public string JavaSimpleName => support.JavaSimpleName; @@ -718,7 +721,7 @@ public string [] PreCallback (CodeGenerationOptions opt, string var_name, bool o public string [] PostCallback (CodeGenerationOptions opt, string var_name) => new string [] { }; - public string RawJniName => PackageName.Replace ('.', '/') + "/" + JavaSimpleName.Replace ('.', '$'); + public string RawJniName => (string.IsNullOrWhiteSpace (PackageName) ? string.Empty : PackageName.Replace ('.', '/') + "/") + JavaSimpleName.Replace ('.', '$'); public string RawVisibility => support.Visibility; diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/InterfaceGen.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/InterfaceGen.cs index 28d3acd68..f4dc45488 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/InterfaceGen.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/InterfaceGen.cs @@ -46,7 +46,7 @@ public override string FromNative (CodeGenerationOptions opt, string varname, bo public override void FixupAccessModifiers (CodeGenerationOptions opt) { if (!IsAnnotation) { - foreach (var implementedInterface in ImplementedInterfaces) { + foreach (var implementedInterface in ImplementedInterfaces) { if (string.IsNullOrEmpty (implementedInterface)) { System.Diagnostics.Debug.Assert (false, "BUGBUG - We should never have an empty or null string added on the implemented interface list."); continue; @@ -62,7 +62,7 @@ public override void FixupAccessModifiers (CodeGenerationOptions opt) } } } - + base.FixupAccessModifiers (opt); } @@ -77,13 +77,18 @@ public override void Generate (CodeGenerationOptions opt, GenerationInfo gen_inf sw.WriteLine ("using Java.Interop;"); } sw.WriteLine (); - sw.WriteLine ("namespace {0} {{", Namespace); - sw.WriteLine (); + var hasNamespace = !string.IsNullOrWhiteSpace (Namespace); + if (hasNamespace) { + sw.WriteLine ("namespace {0} {{", Namespace); + sw.WriteLine (); + } var generator = opt.CreateCodeGenerator (sw); - generator.WriteInterface (this, "\t", gen_info); + generator.WriteInterface (this, hasNamespace ? "\t" : string.Empty, gen_info); - sw.WriteLine ("}"); + if (hasNamespace) { + sw.WriteLine ("}"); + } } GenerateAnnotationAttribute (opt, gen_info); diff --git a/tools/generator/Tests/expected.ji/TestInterface/Mono.Android.projitems b/tools/generator/Tests/expected.ji/TestInterface/Mono.Android.projitems index 3cca37503..5a3e90231 100644 --- a/tools/generator/Tests/expected.ji/TestInterface/Mono.Android.projitems +++ b/tools/generator/Tests/expected.ji/TestInterface/Mono.Android.projitems @@ -5,6 +5,8 @@ + + diff --git a/tools/generator/Tests/expected.ji/TestInterface/__NamespaceMapping__.cs b/tools/generator/Tests/expected.ji/TestInterface/__NamespaceMapping__.cs index 7e74e34c4..2450609ca 100644 --- a/tools/generator/Tests/expected.ji/TestInterface/__NamespaceMapping__.cs +++ b/tools/generator/Tests/expected.ji/TestInterface/__NamespaceMapping__.cs @@ -1,2 +1,3 @@ [assembly:global::Android.Runtime.NamespaceMapping (Java = "java.lang", Managed="Java.Lang")] [assembly:global::Android.Runtime.NamespaceMapping (Java = "test.me", Managed="Test.ME")] +[assembly:global::Android.Runtime.NamespaceMapping (Java = "", Managed="")] diff --git a/tools/generator/Tests/expected/TestInterface/ClassWithoutNamespace.cs b/tools/generator/Tests/expected/TestInterface/ClassWithoutNamespace.cs new file mode 100644 index 000000000..05719a261 --- /dev/null +++ b/tools/generator/Tests/expected/TestInterface/ClassWithoutNamespace.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using Android.Runtime; + +// Metadata.xml XPath class reference: path="/api/package[@name='']/class[@name='ClassWithoutNamespace']" +[global::Android.Runtime.Register ("ClassWithoutNamespace", DoNotGenerateAcw=true)] +public abstract partial class ClassWithoutNamespace : global::Java.Lang.Object, IInterfaceWithoutNamespace { + + internal static new IntPtr java_class_handle; + internal static new IntPtr class_ref { + get { + return JNIEnv.FindClass ("ClassWithoutNamespace", ref java_class_handle); + } + } + + protected override IntPtr ThresholdClass { + get { return class_ref; } + } + + protected override global::System.Type ThresholdType { + get { return typeof (ClassWithoutNamespace); } + } + + protected ClassWithoutNamespace (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {} + + static IntPtr id_ctor; + // Metadata.xml XPath constructor reference: path="/api/package[@name='']/class[@name='ClassWithoutNamespace']/constructor[@name='ClassWithoutNamespace' and count(parameter)=0]" + [Register (".ctor", "()V", "")] + public unsafe ClassWithoutNamespace () + : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) + { + if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero) + return; + + try { + if (((object) this).GetType () != typeof (ClassWithoutNamespace)) { + SetHandle ( + global::Android.Runtime.JNIEnv.StartCreateInstance (((object) this).GetType (), "()V"), + JniHandleOwnership.TransferLocalRef); + global::Android.Runtime.JNIEnv.FinishCreateInstance (((global::Java.Lang.Object) this).Handle, "()V"); + return; + } + + if (id_ctor == IntPtr.Zero) + id_ctor = JNIEnv.GetMethodID (class_ref, "", "()V"); + SetHandle ( + global::Android.Runtime.JNIEnv.StartCreateInstance (class_ref, id_ctor), + JniHandleOwnership.TransferLocalRef); + JNIEnv.FinishCreateInstance (((global::Java.Lang.Object) this).Handle, class_ref, id_ctor); + } finally { + } + } + + static Delegate cb_Foo; +#pragma warning disable 0169 + static Delegate GetFooHandler () + { + if (cb_Foo == null) + cb_Foo = JNINativeWrapper.CreateDelegate ((Action) n_Foo); + return cb_Foo; + } + + static void n_Foo (IntPtr jnienv, IntPtr native__this) + { + ClassWithoutNamespace __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer); + __this.Foo (); + } +#pragma warning restore 0169 + + // Metadata.xml XPath method reference: path="/api/package[@name='']/interface[@name='InterfaceWithoutNamespace']/method[@name='Foo' and count(parameter)=0]" + [Register ("Foo", "()V", "GetFooHandler")] + public abstract void Foo (); + +} + +[global::Android.Runtime.Register ("ClassWithoutNamespace", DoNotGenerateAcw=true)] +internal partial class ClassWithoutNamespaceInvoker : ClassWithoutNamespace { + + public ClassWithoutNamespaceInvoker (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) {} + + protected override global::System.Type ThresholdType { + get { return typeof (ClassWithoutNamespaceInvoker); } + } + + static IntPtr id_Foo; + // Metadata.xml XPath method reference: path="/api/package[@name='']/interface[@name='InterfaceWithoutNamespace']/method[@name='Foo' and count(parameter)=0]" + [Register ("Foo", "()V", "GetFooHandler")] + public override unsafe void Foo () + { + if (id_Foo == IntPtr.Zero) + id_Foo = JNIEnv.GetMethodID (class_ref, "Foo", "()V"); + try { + JNIEnv.CallVoidMethod (((global::Java.Lang.Object) this).Handle, id_Foo); + } finally { + } + } + +} + diff --git a/tools/generator/Tests/expected/TestInterface/IInterfaceWithoutNamespace.cs b/tools/generator/Tests/expected/TestInterface/IInterfaceWithoutNamespace.cs new file mode 100644 index 000000000..718a0dc92 --- /dev/null +++ b/tools/generator/Tests/expected/TestInterface/IInterfaceWithoutNamespace.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Android.Runtime; + +// Metadata.xml XPath interface reference: path="/api/package[@name='']/interface[@name='InterfaceWithoutNamespace']" +[Register ("InterfaceWithoutNamespace", "", "IInterfaceWithoutNamespaceInvoker")] +public partial interface IInterfaceWithoutNamespace : IJavaObject { + + // Metadata.xml XPath method reference: path="/api/package[@name='']/interface[@name='InterfaceWithoutNamespace']/method[@name='Foo' and count(parameter)=0]" + [Register ("Foo", "()V", "GetFooHandler:IInterfaceWithoutNamespaceInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")] + void Foo (); + +} + +[global::Android.Runtime.Register ("InterfaceWithoutNamespace", DoNotGenerateAcw=true)] +internal partial class IInterfaceWithoutNamespaceInvoker : global::Java.Lang.Object, IInterfaceWithoutNamespace { + + static IntPtr java_class_ref = JNIEnv.FindClass ("InterfaceWithoutNamespace"); + + protected override IntPtr ThresholdClass { + get { return class_ref; } + } + + protected override global::System.Type ThresholdType { + get { return typeof (IInterfaceWithoutNamespaceInvoker); } + } + + new IntPtr class_ref; + + public static IInterfaceWithoutNamespace GetObject (IntPtr handle, JniHandleOwnership transfer) + { + return global::Java.Lang.Object.GetObject (handle, transfer); + } + + static IntPtr Validate (IntPtr handle) + { + if (!JNIEnv.IsInstanceOf (handle, java_class_ref)) + throw new InvalidCastException (string.Format ("Unable to convert instance of type '{0}' to type '{1}'.", + JNIEnv.GetClassNameFromInstance (handle), "InterfaceWithoutNamespace")); + return handle; + } + + protected override void Dispose (bool disposing) + { + if (this.class_ref != IntPtr.Zero) + JNIEnv.DeleteGlobalRef (this.class_ref); + this.class_ref = IntPtr.Zero; + base.Dispose (disposing); + } + + public IInterfaceWithoutNamespaceInvoker (IntPtr handle, JniHandleOwnership transfer) : base (Validate (handle), transfer) + { + IntPtr local_ref = JNIEnv.GetObjectClass (((global::Java.Lang.Object) this).Handle); + this.class_ref = JNIEnv.NewGlobalRef (local_ref); + JNIEnv.DeleteLocalRef (local_ref); + } + + static Delegate cb_Foo; +#pragma warning disable 0169 + static Delegate GetFooHandler () + { + if (cb_Foo == null) + cb_Foo = JNINativeWrapper.CreateDelegate ((Action) n_Foo); + return cb_Foo; + } + + static void n_Foo (IntPtr jnienv, IntPtr native__this) + { + IInterfaceWithoutNamespace __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer); + __this.Foo (); + } +#pragma warning restore 0169 + + IntPtr id_Foo; + public unsafe void Foo () + { + if (id_Foo == IntPtr.Zero) + id_Foo = JNIEnv.GetMethodID (class_ref, "Foo", "()V"); + JNIEnv.CallVoidMethod (((global::Java.Lang.Object) this).Handle, id_Foo); + } + +} + diff --git a/tools/generator/Tests/expected/TestInterface/TestInterface.xml b/tools/generator/Tests/expected/TestInterface/TestInterface.xml index be347260b..0357601e7 100644 --- a/tools/generator/Tests/expected/TestInterface/TestInterface.xml +++ b/tools/generator/Tests/expected/TestInterface/TestInterface.xml @@ -50,7 +50,7 @@ -