From 706b3da65f4a07c92885aebb1ee90494ef527033 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 29 Nov 2024 01:14:38 +0100 Subject: [PATCH] [Foundation] Improve the NSAttributedString constructors. Managed constructors can't fail gracefully, which means that constructors with an "out NSError" parameter doesn't make much sense. Instead bind these constructors using a factory method. Ref: https://github.com/xamarin/xamarin-macios/pull/16804#issuecomment-2500025776 --- src/Foundation/NSAttributedString.cs | 52 +++++++++++++++++++ src/Foundation/NSObject2.cs | 12 +++-- src/foundation.cs | 32 +++++++++--- .../Documentation.KnownFailures.txt | 4 -- .../Foundation/AttributedStringTest.cs | 38 ++++++++++++++ 5 files changed, 125 insertions(+), 13 deletions(-) diff --git a/src/Foundation/NSAttributedString.cs b/src/Foundation/NSAttributedString.cs index f57ce4070159..e94e05f93aee 100644 --- a/src/Foundation/NSAttributedString.cs +++ b/src/Foundation/NSAttributedString.cs @@ -41,6 +41,58 @@ namespace Foundation { public partial class NSAttributedString { + /// Create a new . + /// A url to the document to load. + /// A dictionary of attributes that specifies how to interpret the document contents. + /// Upon return, a dictionary of document-specific keys. + /// The error if an error occurred. + public static NSAttributedString? Create (NSUrl url, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error) + { + var rv = new NSAttributedString (NSObjectFlag.Empty); + rv.InitializeHandle (rv._InitWithUrl (url, options, out resultDocumentAttributes, out error), string.Empty, false); + if (rv.Handle == IntPtr.Zero) { + rv.Dispose (); + return null; + } + return rv; + } + + /// Create a new . + /// A url to the document to load. + /// A dictionary of attributes that specifies how to interpret the document contents. + /// Upon return, a dictionary of document-specific keys. + /// The error if an error occurred. + public static NSAttributedString? Create (NSUrl url, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, out NSError error) + { + return Create (url, options.Dictionary, out resultDocumentAttributes, out error); + } + + /// Create a new . + /// The data to load. + /// A dictionary of attributes that specifies how to interpret the document contents. + /// Upon return, a dictionary of document-specific keys. + /// The error if an error occurred. + public static NSAttributedString? Create (NSData data, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error) + { + var rv = new NSAttributedString (NSObjectFlag.Empty); + rv.InitializeHandle (rv._InitWithData (data, options, out resultDocumentAttributes, out error), string.Empty, false); + if (rv.Handle == IntPtr.Zero) { + rv.Dispose (); + return null; + } + return rv; + } + + /// Create a new . + /// The data to load. + /// A dictionary of attributes that specifies how to interpret the document contents. + /// Upon return, a dictionary of document-specific keys. + /// The error if an error occurred. + public static NSAttributedString? Create (NSData data, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, out NSError error) + { + return Create (data, options.Dictionary, out resultDocumentAttributes, out error); + } + #if __MACOS__ || XAMCORE_5_0 public NSAttributedString (NSUrl url, NSAttributedStringDocumentAttributes documentAttributes, out NSError error) : this (url, documentAttributes, out var _, out error) {} diff --git a/src/Foundation/NSObject2.cs b/src/Foundation/NSObject2.cs index 16dd2dd38dda..3c976d19de92 100644 --- a/src/Foundation/NSObject2.cs +++ b/src/Foundation/NSObject2.cs @@ -665,19 +665,25 @@ public NativeHandle Handle { [EditorBrowsable (EditorBrowsableState.Never)] protected void InitializeHandle (NativeHandle handle) { - InitializeHandle (handle, "init*"); + InitializeHandle (handle, "init*", Class.ThrowOnInitFailure); } [EditorBrowsable (EditorBrowsableState.Never)] protected void InitializeHandle (NativeHandle handle, string initSelector) { - if (this.handle == NativeHandle.Zero && Class.ThrowOnInitFailure) { + InitializeHandle (handle, initSelector, Class.ThrowOnInitFailure); + } + + [EditorBrowsable (EditorBrowsableState.Never)] + internal void InitializeHandle (NativeHandle handle, string initSelector, bool throwOnInitFailure) + { + if (this.handle == NativeHandle.Zero && throwOnInitFailure) { if (ClassHandle == NativeHandle.Zero) throw new Exception ($"Could not create an native instance of the type '{GetType ().FullName}': the native class hasn't been loaded.\n{Constants.SetThrowOnInitFailureToFalse}."); throw new Exception ($"Could not create an native instance of the type '{new Class (ClassHandle).Name}'.\n{Constants.SetThrowOnInitFailureToFalse}."); } - if (handle == NativeHandle.Zero && Class.ThrowOnInitFailure) { + if (handle == NativeHandle.Zero && throwOnInitFailure) { Handle = NativeHandle.Zero; // We'll crash if we don't do this. throw new Exception ($"Could not initialize an instance of the type '{GetType ().FullName}': the native '{initSelector}' method returned nil.\n{Constants.SetThrowOnInitFailureToFalse}."); } diff --git a/src/foundation.cs b/src/foundation.cs index c74a77d22a0e..339f7e0d8d6d 100644 --- a/src/foundation.cs +++ b/src/foundation.cs @@ -354,37 +354,57 @@ partial interface NSAttributedString : NSCoding, NSMutableCopying, NSSecureCodin [Export ("enumerateAttribute:inRange:options:usingBlock:")] void EnumerateAttribute (NSString attributeName, NSRange inRange, NSAttributedStringEnumeration options, NSAttributedStringCallback callback); +#if !XAMCORE_5_0 + [Obsolete ("Use the 'Create' method instead, because there's no way to return an error from a constructor.")] [Export ("initWithURL:options:documentAttributes:error:")] -#if !(__MACOS__ || XAMCORE_5_0) +#if !__MACOS__ NativeHandle Constructor (NSUrl url, NSDictionary options, out NSDictionary resultDocumentAttributes, ref NSError error); #else NativeHandle Constructor (NSUrl url, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error); #endif +#endif // !XAMCORE_5_0 + [Internal] + [Sealed] + [Export ("initWithURL:options:documentAttributes:error:")] + NativeHandle _InitWithUrl (NSUrl url, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error); + +#if !XAMCORE_5_0 + [Obsolete ("Use the 'Create' method instead, because there's no way to return an error from a constructor.")] [Export ("initWithData:options:documentAttributes:error:")] -#if XAMCORE_5_0 - NativeHandle Constructor (NSData data, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error); -#elif __MACOS__ +#if __MACOS__ NativeHandle Constructor (NSData data, NSDictionary options, out NSDictionary docAttributes, out NSError error); #else NativeHandle Constructor (NSData data, NSDictionary options, out NSDictionary resultDocumentAttributes, ref NSError error); #endif +#endif // !XAMCORE_5_0 -#if __MACOS__ || XAMCORE_5_0 + [Internal] + [Sealed] + [Export ("initWithData:options:documentAttributes:error:")] + NativeHandle _InitWithData (NSData data, NSDictionary options, out NSDictionary resultDocumentAttributes, out NSError error); + +#if !XAMCORE_5_0 + [Obsolete ("Use the 'Create' method instead, because there's no way to return an error from a constructor.")] +#if __MACOS__ [Wrap ("this (url, options.GetDictionary ()!, out resultDocumentAttributes, out error)")] NativeHandle Constructor (NSUrl url, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, out NSError error); #else [Wrap ("this (url, options.GetDictionary ()!, out resultDocumentAttributes, ref error)")] NativeHandle Constructor (NSUrl url, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, ref NSError error); #endif +#endif // !XAMCORE_5_0 -#if __MACOS__ || XAMCORE_5_0 + [Obsolete ("Use the 'Create' method instead, because there's no way to return an error from a constructor.")] +#if !XAMCORE_5_0 +#if __MACOS__ [Wrap ("this (data, options.GetDictionary ()!, out resultDocumentAttributes, out error)")] NativeHandle Constructor (NSData data, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, out NSError error); #else [Wrap ("this (data, options.GetDictionary ()!, out resultDocumentAttributes, ref error)")] NativeHandle Constructor (NSData data, NSAttributedStringDocumentAttributes options, out NSDictionary resultDocumentAttributes, ref NSError error); #endif +#endif // !XAMCORE_5_0 [NoiOS] [NoMacCatalyst] diff --git a/tests/cecil-tests/Documentation.KnownFailures.txt b/tests/cecil-tests/Documentation.KnownFailures.txt index f296c9381ed6..aabe21aed218 100644 --- a/tests/cecil-tests/Documentation.KnownFailures.txt +++ b/tests/cecil-tests/Documentation.KnownFailures.txt @@ -33263,18 +33263,14 @@ M:Foundation.NSArray`1.#ctor(Foundation.NSCoder) M:Foundation.NSArray`1.FromNSObjects(`0[]) M:Foundation.NSArray`1.FromNSObjects(System.Int32,`0[]) M:Foundation.NSArray`1.ToArray -M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSAttributedStringDocumentAttributes,Foundation.NSDictionary@,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSAttributedStringDocumentAttributes,Foundation.NSDictionary@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSAttributedStringDocumentAttributes,Foundation.NSError@) -M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSDictionary,Foundation.NSDictionary@,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSDictionary,Foundation.NSDictionary@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSDictionary@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(Foundation.NSData,Foundation.NSUrl,Foundation.NSDictionary@) M:Foundation.NSAttributedString.#ctor(Foundation.NSFileWrapper,Foundation.NSDictionary@) -M:Foundation.NSAttributedString.#ctor(Foundation.NSUrl,Foundation.NSAttributedStringDocumentAttributes,Foundation.NSDictionary@,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(Foundation.NSUrl,Foundation.NSAttributedStringDocumentAttributes,Foundation.NSError@) -M:Foundation.NSAttributedString.#ctor(Foundation.NSUrl,Foundation.NSDictionary,Foundation.NSDictionary@,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(Foundation.NSUrl,Foundation.NSDictionary@) M:Foundation.NSAttributedString.#ctor(Foundation.NSUrl,Foundation.NSError@) M:Foundation.NSAttributedString.#ctor(System.String,AppKit.NSFont,AppKit.NSColor,AppKit.NSColor,AppKit.NSColor,AppKit.NSColor,AppKit.NSColor,Foundation.NSUnderlineStyle,Foundation.NSUnderlineStyle,AppKit.NSParagraphStyle,System.Single,AppKit.NSShadow,Foundation.NSUrl,System.Boolean,AppKit.NSTextAttachment,Foundation.NSLigatureType,System.Single,System.Single,System.Single,System.Single,AppKit.NSCursor,System.String,System.Int32,AppKit.NSGlyphInfo,Foundation.NSArray,System.Boolean,AppKit.NSTextLayoutOrientation,AppKit.NSTextAlternatives,AppKit.NSSpellingState) diff --git a/tests/monotouch-test/Foundation/AttributedStringTest.cs b/tests/monotouch-test/Foundation/AttributedStringTest.cs index 749d05cc3204..04b68547fd28 100644 --- a/tests/monotouch-test/Foundation/AttributedStringTest.cs +++ b/tests/monotouch-test/Foundation/AttributedStringTest.cs @@ -1,4 +1,6 @@ using System; +using System.IO; + using NUnit.Framework; using Foundation; #if MONOMAC @@ -135,6 +137,42 @@ public void NullDictionary () } } + [Test] + public void Create_Url_Error () + { + var obj = NSAttributedString.Create (new NSUrl (""), new NSAttributedStringDocumentAttributes (), out var rda, out var e); + Assert.IsNull (obj, "IsNull"); + Assert.IsNotNull (e, "Error"); + } + + [Test] + public void Create_Url () + { + var textFile = Path.Combine (NSBundle.MainBundle.ResourcePath, "uncompressed.txt"); + var textUrl = NSUrl.CreateFileUrl (textFile); + var obj = NSAttributedString.Create (textUrl, new NSAttributedStringDocumentAttributes (), out var rda, out var e); + Assert.IsNull (e, "Error"); + Assert.IsNotNull (obj, "IsNull"); + } + + [Test] + public void Create_Data_Error () + { + var attributes = new NSAttributedStringDocumentAttributes (); + attributes.DocumentType = NSDocumentType.RTF; + var obj = NSAttributedString.Create (NSData.FromArray (new byte [42]), attributes, out var rda, out var e); + Assert.IsNull (obj, "IsNull"); + Assert.IsNotNull (e, "Error"); + } + + [Test] + public void Create_Data () + { + var obj = NSAttributedString.Create (new NSData (), new NSAttributedStringDocumentAttributes (), out var rda, out var e); + Assert.IsNotNull (obj, "IsNull"); + Assert.IsNull (e, "Error"); + } + #if !__WATCHOS__ [Test] public void IndirectNullDictionary ()