diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Extensions/XmlExtensions.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Extensions/XmlExtensions.cs new file mode 100644 index 000000000..a22e1c4f3 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Extensions/XmlExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Xml; +using System.Xml.Linq; + +namespace Java.Interop.Tools.JavaCallableWrappers.Extensions; + +static class XmlExtensions +{ + public static T GetAttributeOrDefault (this XElement xml, string name, T defaultValue) + { + var value = xml.Attribute (name)?.Value; + + if (string.IsNullOrWhiteSpace (value)) + return defaultValue; + + return (T) Convert.ChangeType (value, typeof (T)); + } + + public static string GetRequiredAttribute (this XElement xml, string name) + { + var value = xml.Attribute (name)?.Value; + + if (string.IsNullOrWhiteSpace (value)) + throw new InvalidOperationException ($"Missing required attribute '{name}'"); + + return value!; // NRT - Guarded by IsNullOrWhiteSpace check above + } + + public static void WriteAttributeStringIfNotNull (this XmlWriter xml, string name, string? value) + { + if (value is not null) + xml.WriteAttributeString (name, value); + } + + public static void WriteAttributeStringIfNotFalse (this XmlWriter xml, string name, bool value) + { + // If value is false, don't write the attribute, we'll default to false on import + if (value) + xml.WriteAttributeString (name, value.ToString ()); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlExporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlExporter.cs new file mode 100644 index 000000000..6b2743e12 --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlExporter.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; +using Java.Interop.Tools.JavaCallableWrappers.Extensions; + +namespace Java.Interop.Tools.JavaCallableWrappers.Adapters; + +public static class XmlExporter +{ + static XmlWriterSettings settings = new XmlWriterSettings { + Indent = true, + NewLineOnAttributes = false, + OmitXmlDeclaration = true, + }; + + public static void Export (string filename, IEnumerable types, bool wasScanned) + { + using (var sw = new StreamWriter (filename, false, Encoding.UTF8)) + Export (sw, types, wasScanned); + } + + public static void Export (TextWriter sw, IEnumerable types, bool wasScanned) + { + using (var xml = XmlWriter.Create (sw, settings)) + Export (xml, types, wasScanned); + } + + public static void Export (XmlWriter xml, IEnumerable types, bool wasScanned) + { + ExportTypes (xml, types, wasScanned); + } + + static void ExportTypes (XmlWriter xml, IEnumerable types, bool wasScanned) + { + xml.WriteStartElement ("types"); + xml.WriteAttributeString ("was_scanned", wasScanned.ToString ()); + + foreach (var type in types) + ExportType (xml, type); + + xml.WriteEndElement (); + } + + public static void ExportType (XmlWriter xml, CallableWrapperType type) + { + xml.WriteStartElement ("type"); + xml.WriteAttributeString ("name", type.Name); + xml.WriteAttributeString ("package", type.Package); + xml.WriteAttributeStringIfNotFalse ("is_abstract", type.IsAbstract); + xml.WriteAttributeStringIfNotNull ("application_java_class", type.ApplicationJavaClass); + xml.WriteAttributeStringIfNotFalse ("generate_on_create_overrides", type.GenerateOnCreateOverrides); + xml.WriteAttributeStringIfNotNull ("mono_runtime_initialization", type.MonoRuntimeInitialization); + xml.WriteAttributeStringIfNotNull ("extends_type", type.ExtendsType); + xml.WriteAttributeStringIfNotFalse ("is_application", type.IsApplication); + xml.WriteAttributeStringIfNotFalse ("is_instrumentation", type.IsInstrumentation); + xml.WriteAttributeString ("partial_assembly_qualified_name", type.PartialAssemblyQualifiedName); + xml.WriteAttributeStringIfNotFalse ("has_export", type.HasExport); + + if (type.ApplicationConstructor is not null) + xml.WriteAttributeString ("application_constructor", type.ApplicationConstructor.Name); + + ExportAnnotations (xml, type.Annotations); + ExportImplementedInterfaces (xml, type.ImplementedInterfaces); + ExportConstructors (xml, type.Constructors); + ExportFields (xml, type.Fields); + ExportMethods (xml, type.Methods); + ExportNestedTypes (xml, type.NestedTypes); + + xml.WriteEndElement (); + } + + static void ExportAnnotations (XmlWriter writer, IEnumerable annotations) + { + if (annotations.Count () == 0) + return; + + writer.WriteStartElement ("annotations"); + + foreach (var annotation in annotations) { + writer.WriteStartElement ("annotation"); + writer.WriteAttributeString ("name", annotation.Name); + + foreach (var property in annotation.Properties) { + writer.WriteStartElement ("property"); + writer.WriteAttributeString ("name", property.Key); + writer.WriteAttributeString ("value", property.Value); + writer.WriteEndElement (); + } + + writer.WriteEndElement (); + } + + writer.WriteEndElement (); + } + + static void ExportImplementedInterfaces (XmlWriter writer, List interfaces) + { + if (interfaces.Count == 0) + return; + + writer.WriteStartElement ("implemented_interfaces"); + + foreach (var @interface in interfaces) { + writer.WriteStartElement ("interface"); + writer.WriteAttributeString ("name", @interface); + writer.WriteEndElement (); + } + + writer.WriteEndElement (); + } + + static void ExportConstructors (XmlWriter xml, IEnumerable constructors) + { + if (constructors.Count () == 0) + return; + + xml.WriteStartElement ("constructors"); + + foreach (var constructor in constructors) + ExportMethod (xml, constructor); + + xml.WriteEndElement (); + } + + static void ExportFields (XmlWriter xml, IEnumerable fields) + { + if (fields.Count () == 0) + return; + + xml.WriteStartElement ("fields"); + + foreach (var field in fields) { + xml.WriteStartElement ("field"); + xml.WriteAttributeString ("name", field.FieldName); + xml.WriteAttributeString ("type", field.TypeName); + xml.WriteAttributeString ("visibility", field.Visibility); + xml.WriteAttributeStringIfNotFalse ("is_static", field.IsStatic); + xml.WriteAttributeString ("initializer_name", field.InitializerName); + ExportAnnotations (xml, field.Annotations); + xml.WriteEndElement (); + } + + xml.WriteEndElement (); + } + + static void ExportMethods (XmlWriter xml, IEnumerable methods) + { + if (methods.Count () == 0) + return; + + xml.WriteStartElement ("methods"); + + foreach (var method in methods) + ExportMethod (xml, method); + + xml.WriteEndElement (); + } + + static void ExportMethod (XmlWriter xml, CallableWrapperMethod method) + { + xml.WriteStartElement (method is CallableWrapperConstructor ? "constructor" : "method"); + xml.WriteAttributeString ("name", method.Name); + xml.WriteAttributeString ("method", method.Method); + xml.WriteAttributeString ("jni_signature", method.JniSignature); + xml.WriteAttributeStringIfNotNull ("managed_parameters", method.ManagedParameters); + xml.WriteAttributeStringIfNotNull ("java_name_override", method.JavaNameOverride); + xml.WriteAttributeStringIfNotNull ("params", method.Params); + xml.WriteAttributeStringIfNotNull ("retval", method.Retval); + xml.WriteAttributeStringIfNotNull ("java_access", method.JavaAccess); + xml.WriteAttributeStringIfNotFalse ("is_export", method.IsExport); + xml.WriteAttributeStringIfNotFalse ("is_static", method.IsStatic); + xml.WriteAttributeStringIfNotFalse ("is_dynamically_registered", method.IsDynamicallyRegistered); + xml.WriteAttributeStringIfNotNull ("thrown_type_names", method.ThrownTypeNames != null ? string.Join (", ", method.ThrownTypeNames) : null); + xml.WriteAttributeStringIfNotNull ("super_call", method.SuperCall); + xml.WriteAttributeStringIfNotNull ("activate_call", method.ActivateCall); + + ExportAnnotations (xml, method.Annotations); + + xml.WriteEndElement (); + } + + static void ExportNestedTypes (XmlWriter xml, IEnumerable nestedTypes) + { + if (nestedTypes.Count () == 0) + return; + + xml.WriteStartElement ("nested_types"); + + foreach (var nestedType in nestedTypes) + ExportType (xml, nestedType); + + xml.WriteEndElement (); + } +} diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlImporter.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlImporter.cs new file mode 100644 index 000000000..07846bfaa --- /dev/null +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/XmlImporter.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using Java.Interop.Tools.JavaCallableWrappers.CallableWrapperMembers; +using Java.Interop.Tools.JavaCallableWrappers.Extensions; + +namespace Java.Interop.Tools.JavaCallableWrappers.Adapters; + +public static class XmlImporter +{ + public static List Import (string filename, out bool wasScanned) + { + using (var sr = new StreamReader (filename, Encoding.UTF8)) + return Import (sr, out wasScanned); + } + + public static List Import (TextReader sr, out bool wasScanned) + { + using (var xml = XmlReader.Create (sr)) + return Import (xml, out wasScanned); + } + + public static List Import (XmlReader xml, out bool wasScanned) + { + var doc = XDocument.Load (xml); + + var types = new List (); + wasScanned = doc.Root.GetAttributeOrDefault ("was_scanned", false); + + foreach (var type in doc.Root.Elements ("type")) + types.Add (ImportType (type)); + + return types; + } + + public static CallableWrapperType ImportType (XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var package = xml.GetRequiredAttribute ("package"); + var partial_assembly_qualified_name = xml.GetRequiredAttribute ("partial_assembly_qualified_name"); + + var type = new CallableWrapperType (name, package, partial_assembly_qualified_name) { + ApplicationJavaClass = xml.GetAttributeOrDefault ("application_java_class", (string?) null), + ExtendsType = xml.GetAttributeOrDefault ("extends_type", (string?) null), + GenerateOnCreateOverrides = xml.GetAttributeOrDefault ("generate_on_create_overrides", false), + HasExport = xml.GetAttributeOrDefault ("has_export", false), + IsAbstract = xml.GetAttributeOrDefault ("is_abstract", false), + IsApplication = xml.GetAttributeOrDefault ("is_application", false), + IsInstrumentation = xml.GetAttributeOrDefault ("is_instrumentation", false), + MonoRuntimeInitialization = xml.GetAttributeOrDefault ("mono_runtime_initialization", (string?) null), + }; + + if (xml.GetAttributeOrDefault ("application_constructor", (string?) null) is string applicationConstructor) + type.ApplicationConstructor = new CallableWrapperApplicationConstructor (applicationConstructor); + + ImportAnnotations (type.Annotations, xml.Element ("annotations")); + ImportImplementedInterfaces (type, xml.Element ("implemented_interfaces")); + ImportConstructors (type, xml.Element ("constructors")); + ImportMethods (type, xml.Element ("methods")); + ImportFields (type, xml.Element ("fields")); + + foreach (var nestedType in xml.Elements ("nested_type")) + type.NestedTypes.Add (ImportType (nestedType)); + + return type; + } + + static void ImportAnnotations (List annotations, XElement? xml) + { + foreach (var annotation in xml?.Elements ("annotation") ?? []) { + var a = ImportAnnotation (annotation); + annotations.Add (a); + } + } + + static CallableWrapperTypeAnnotation ImportAnnotation (XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var annotation = new CallableWrapperTypeAnnotation (name); + + foreach (var property in xml.Elements ("property")) { + var p = ImportAnnotationProperty (property); + annotation.Properties.Add (p); + } + + return annotation; + } + + static KeyValuePair ImportAnnotationProperty (XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var value = xml.GetRequiredAttribute ("value"); + + return new KeyValuePair (name, value); + } + + static void ImportImplementedInterfaces (CallableWrapperType type, XElement? xml) + { + foreach (var iface in xml?.Elements ("interface") ?? []) { + var name = iface.GetRequiredAttribute ("name"); + type.ImplementedInterfaces.Add (name); + } + } + + static void ImportConstructors (CallableWrapperType type, XElement? xml) + { + foreach (var ctor in xml?.Elements ("constructor") ?? []) { + var c = ImportConstructor (type, ctor); + type.Constructors.Add (c); + } + } + + static void ImportMethods (CallableWrapperType type, XElement? xml) + { + foreach (var method in xml?.Elements ("method") ?? []) { + var m = ImportMethod (type, method); + type.Methods.Add (m); + } + } + + static CallableWrapperConstructor ImportConstructor (CallableWrapperType type, XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var method = xml.GetRequiredAttribute ("method"); + var jniSig = xml.GetRequiredAttribute ("jni_signature"); + + var ctor = new CallableWrapperConstructor (type, name, method, jniSig); + FillInMethodDetails (ctor, xml); + + return ctor; + } + + static CallableWrapperMethod ImportMethod (CallableWrapperType type, XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var method = xml.GetRequiredAttribute ("method"); + var jniSig = xml.GetRequiredAttribute ("jni_signature"); + + var m = new CallableWrapperMethod (type, name, method, jniSig); + FillInMethodDetails (m, xml); + + return m; + } + + static void FillInMethodDetails (CallableWrapperMethod method, XElement xml) + { + // Common between constructors and methods + method.ManagedParameters = xml.GetAttributeOrDefault ("managed_parameters", (string?) null); + method.JavaNameOverride = xml.GetAttributeOrDefault ("java_name_override", (string?) null); + method.Params = xml.GetAttributeOrDefault ("params", (string?) null); + method.Retval = xml.GetAttributeOrDefault ("retval", (string?) null); + method.JavaAccess = xml.GetAttributeOrDefault ("java_access", (string?) null); + method.IsExport = xml.GetAttributeOrDefault ("is_export", false); + method.IsStatic = xml.GetAttributeOrDefault ("is_static", false); + method.IsDynamicallyRegistered = xml.GetAttributeOrDefault ("is_dynamically_registered", false); + method.SuperCall = xml.GetAttributeOrDefault ("super_call", (string?) null); + method.ActivateCall = xml.GetAttributeOrDefault ("activate_call", (string?) null); + + if (xml.GetAttributeOrDefault ("thrown_type_names", (string?) null) is string thrownTypeNames) + method.ThrownTypeNames = thrownTypeNames.Split (new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + ImportAnnotations (method.Annotations, xml.Element ("annotations")); + } + + static void ImportFields (CallableWrapperType type, XElement? xml) + { + foreach (var field in xml?.Elements ("field") ?? []) { + var f = ImportField (field); + type.Fields.Add (f); + } + } + + static CallableWrapperField ImportField (XElement xml) + { + var name = xml.GetRequiredAttribute ("name"); + var type = xml.GetRequiredAttribute ("type"); + var visibility = xml.GetRequiredAttribute ("visibility"); + var initializer_name = xml.GetRequiredAttribute ("initializer_name"); + + var field = new CallableWrapperField (name, type, visibility, initializer_name) { + IsStatic = xml.GetAttributeOrDefault ("is_static", false), + }; + + ImportAnnotations (field.Annotations, xml.Element ("annotations")); + + return field; + } +}