Skip to content

Parity: NSCoding: NSTextCheckingResult #2202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions Foundation/NSRegularExpression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension NSRegularExpression {
}
}

open class NSRegularExpression: NSObject, NSCopying, NSCoding {
open class NSRegularExpression: NSObject, NSCopying, NSSecureCoding {
internal var _internal: _CFRegularExpression

open override func copy() -> Any {
Expand All @@ -44,26 +44,29 @@ open class NSRegularExpression: NSObject, NSCopying, NSCoding {
}

aCoder.encode(self.pattern._nsObject, forKey: "NSPattern")
aCoder.encode(self.options.rawValue._bridgeToObjectiveC(), forKey: "NSOptions")
aCoder.encode(Int64(self.options.rawValue), forKey: "NSOptions")
}

public required convenience init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}

guard let pattern = aDecoder.decodeObject(forKey: "NSPattern") as? NSString,
let options = aDecoder.decodeObject(forKey: "NSOptions") as? NSNumber else {
guard let pattern = aDecoder.decodeObject(of: NSString.self, forKey: "NSPattern") else {
return nil
}

let options = aDecoder.decodeInt64(forKey: "NSOptions")

do {
try self.init(pattern: pattern._swiftObject, options: Options(rawValue: options.uintValue))
try self.init(pattern: pattern._swiftObject, options: Options(rawValue: UInt(options)))
} catch {
return nil
}
}

open class var supportsSecureCoding: Bool { return true }

open override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? NSRegularExpression else { return false }

Expand Down Expand Up @@ -161,7 +164,7 @@ internal func _NSRegularExpressionMatch(_ context: UnsafeMutableRawPointer?, ran
let flags = NSRegularExpression.MatchingFlags(rawValue: flags)
#endif
let result = ranges?.withMemoryRebound(to: NSRange.self, capacity: count) { rangePtr in
NSTextCheckingResult.regularExpressionCheckingResultWithRanges(rangePtr, count: count, regularExpression: matcher.regex)
NSTextCheckingResult.regularExpressionCheckingResult(ranges: rangePtr, count: count, regularExpression: matcher.regex)
}
stop.withMemoryRebound(to: ObjCBool.self, capacity: 1, {
matcher.block(result, flags, $0)
Expand Down
262 changes: 237 additions & 25 deletions Foundation/NSTextCheckingResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@ extension NSTextCheckingResult {
public let rawValue: UInt64
public init(rawValue: UInt64) { self.rawValue = rawValue }

public static let RegularExpression = CheckingType(rawValue: 1 << 10) // regular expression matches
public static let regularExpression = CheckingType(rawValue: 1 << 10)
}
}

open class NSTextCheckingResult: NSObject, NSCopying, NSCoding {
open class NSTextCheckingResult: NSObject, NSCopying, NSSecureCoding {

public override init() {
if type(of: self) == NSTextCheckingResult.self {
NSRequiresConcreteImplementation()
}
}

open class func regularExpressionCheckingResultWithRanges(_ ranges: NSRangePointer, count: Int, regularExpression: NSRegularExpression) -> NSTextCheckingResult {
return _NSRegularExpressionNSTextCheckingResultResult(ranges: ranges, count: count, regularExpression: regularExpression)
open class func regularExpressionCheckingResult(ranges: NSRangePointer, count: Int, regularExpression: NSRegularExpression) -> NSTextCheckingResult {
let buffer = UnsafeBufferPointer(start: ranges, count: count)
let array = Array(buffer)

if count > 0 && count <= 3 {
return NSSimpleRegularExpressionCheckingResult(rangeArray: array, regularExpression: regularExpression)
} else if count > 3 && count <= 7 {
return NSExtendedRegularExpressionCheckingResult(rangeArray: array, regularExpression: regularExpression)
} else {
return NSComplexRegularExpressionCheckingResult(rangeArray: array, regularExpression: regularExpression)
}
}

public required init?(coder aDecoder: NSCoder) {
Expand All @@ -41,6 +50,10 @@ open class NSTextCheckingResult: NSObject, NSCopying, NSCoding {
NSRequiresConcreteImplementation()
}

open class var supportsSecureCoding: Bool {
NSRequiresConcreteImplementation()
}

open override func copy() -> Any {
return copy(with: nil)
}
Expand All @@ -58,44 +71,102 @@ open class NSTextCheckingResult: NSObject, NSCopying, NSCoding {
open func range(withName: String) -> NSRange { NSRequiresConcreteImplementation() }
open var regularExpression: NSRegularExpression? { return nil }
open var numberOfRanges: Int { return 1 }

internal func encodeRange(with coder: NSCoder) {
guard coder.allowsKeyedCoding else {
fatalError("Encoding this class requires keyed coding")
}

coder.encode(range.location, forKey: "NSRangeLocation")
coder.encode(range.length, forKey: "NSRangeLength")
}

internal func decodeRange(from coder: NSCoder) -> NSRange {
guard coder.allowsKeyedCoding else {
fatalError("Decoding this class requires keyed coding")
}

return NSMakeRange(coder.decodeInteger(forKey: "NSRangeLocation"), coder.decodeInteger(forKey: "NSRangeLength"))
}
}

internal class _NSRegularExpressionNSTextCheckingResultResult : NSTextCheckingResult {
var _ranges = [NSRange]()
let _regularExpression: NSRegularExpression
// Darwin uses these private subclasses, each of which can be archived. We reimplement all three here so that NSKeyed{Una,A}rchiver can find them.
// Since we do not have to box an array of NSRanges into a NSArray of NSValues, we do not implement the storage optimization these subclasses would have on Darwin. They exist purely to be encoded or decoded. When we produce instances, we will produce the correct subclass for the count Darwin expects so that _that_ implementation can likewise be efficient.

internal class NSRegularExpressionCheckingResult: NSTextCheckingResult {
let _regularExpression: NSRegularExpression!
override var regularExpression: NSRegularExpression? { return _regularExpression }

init(ranges: NSRangePointer, count: Int, regularExpression: NSRegularExpression) {
let _rangeArray: [NSRange]!
var rangeArray: [NSRange] { return _rangeArray }

init(rangeArray: [NSRange], regularExpression: NSRegularExpression) {
_rangeArray = rangeArray.map { $0.location == kCFNotFound ? NSMakeRange(NSNotFound, 0) : $0 }
_regularExpression = regularExpression
super.init()
let notFound = NSRange(location: NSNotFound,length: 0)
for i in 0..<count {
ranges[i].location == kCFNotFound ? _ranges.append(notFound) : _ranges.append(ranges[i])
}
}

internal required init?(coder aDecoder: NSCoder) {
NSUnimplemented()

override init() {
if type(of: self) == NSRegularExpressionCheckingResult.self {
NSRequiresConcreteImplementation()
}

_regularExpression = nil
_rangeArray = nil
super.init()
}

internal override func encode(with aCoder: NSCoder) {
NSUnimplemented()
public convenience required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
fatalError("Decoding this class requires keyed coding")
}

let regularExpression = aDecoder.decodeObject(of: NSRegularExpression.self, forKey: "NSRegularExpression")!
let nsRanges = aDecoder.decodeObject(of: [ NSArray.self, NSValue.self ], forKey: "NSRangeArray") as! NSArray
let rangeArray = nsRanges.compactMap { return ($0 as! NSValue).rangeValue }

self.init(rangeArray: rangeArray, regularExpression: regularExpression)
}

override func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
fatalError("Encoding this class requires keyed coding")
}

let ranges = rangeArray.map { NSValue(range: $0) }._nsObject

encodeRange(with: aCoder)
aCoder.encode(regularExpression, forKey: "NSRegularExpression")
aCoder.encode(ranges, forKey: "NSRangeArray")
}

override var resultType: CheckingType { return .RegularExpression }
override func range(at idx: Int) -> NSRange { return _ranges[idx] }
override class var supportsSecureCoding: Bool { return true }

override var resultType: NSTextCheckingResult.CheckingType { return .regularExpression }

override func range(withName name: String) -> NSRange {
let idx = _regularExpression._captureGroupNumber(withName: name)
let idx = regularExpression!._captureGroupNumber(withName: name)
if idx != kCFNotFound, idx < numberOfRanges {
return range(at: idx)
}

return NSRange(location: NSNotFound, length: 0)
}

override var numberOfRanges: Int { return _ranges.count }
override var regularExpression: NSRegularExpression? { return _regularExpression }

override func range(at idx: Int) -> NSRange {
return rangeArray[idx]
}

override var numberOfRanges: Int {
return rangeArray.count
}
}

internal class NSSimpleRegularExpressionCheckingResult: NSRegularExpressionCheckingResult {}
internal class NSExtendedRegularExpressionCheckingResult: NSRegularExpressionCheckingResult {}
internal class NSComplexRegularExpressionCheckingResult: NSRegularExpressionCheckingResult {}


extension NSTextCheckingResult {

public func adjustingRanges(offset: Int) -> NSTextCheckingResult {
Expand All @@ -111,8 +182,149 @@ extension NSTextCheckingResult {
newRanges.append(NSRange(location: currentRange.location + offset,length: currentRange.length))
}
}
let result = NSTextCheckingResult.regularExpressionCheckingResultWithRanges(&newRanges, count: count, regularExpression: self.regularExpression!)
let result = NSTextCheckingResult.regularExpressionCheckingResult(ranges: &newRanges, count: count, regularExpression: self.regularExpression!)
return result
}
}

// MARK: Availability diagnostics for unsupported features

@available(*, deprecated, message: "These types of result will not be returned by swift-corelibs-foundation API.")
extension NSTextCheckingResult.CheckingType {
public static let orthography = NSTextCheckingResult.CheckingType(rawValue: 1 << 1)
public static let spelling = NSTextCheckingResult.CheckingType(rawValue: 1 << 2)
public static let grammar = NSTextCheckingResult.CheckingType(rawValue: 1 << 3)
public static let date = NSTextCheckingResult.CheckingType(rawValue: 1 << 4)
public static let address = NSTextCheckingResult.CheckingType(rawValue: 1 << 5)
public static let link = NSTextCheckingResult.CheckingType(rawValue: 1 << 6)
public static let quote = NSTextCheckingResult.CheckingType(rawValue: 1 << 7)
public static let dash = NSTextCheckingResult.CheckingType(rawValue: 1 << 8)
public static let replacement = NSTextCheckingResult.CheckingType(rawValue: 1 << 9)
public static let correction = NSTextCheckingResult.CheckingType(rawValue: 1 << 10)
public static let phoneNumber = NSTextCheckingResult.CheckingType(rawValue: 1 << 11)
public static let transitInformation = NSTextCheckingResult.CheckingType(rawValue: 1 << 12)
}

public struct NSTextCheckingKey: RawRepresentable, Hashable {
public var rawValue: String

init(_ string: String) {
self.init(rawValue: string)
}

public init(rawValue: Self.RawValue) {
self.rawValue = rawValue
}
}

@available(*, deprecated, message: "Results associated with these keys are not available in swift-corelibs-foundation.")
extension NSTextCheckingKey {
static let airline = NSTextCheckingKey(rawValue: "Airline")
static let city = NSTextCheckingKey(rawValue: "City")
static let country = NSTextCheckingKey(rawValue: "Country")
static let flight = NSTextCheckingKey(rawValue: "Flight")
static let jobTitle = NSTextCheckingKey(rawValue: "JobTitle")
static let name = NSTextCheckingKey(rawValue: "Name")
static let organization = NSTextCheckingKey(rawValue: "Organization")
static let phone = NSTextCheckingKey(rawValue: "Phone")
static let state = NSTextCheckingKey(rawValue: "State")
static let street = NSTextCheckingKey(rawValue: "Street")
static let zip = NSTextCheckingKey(rawValue: "Zip")
}

@available(*, unavailable, message: "These types of results cannot be constructed in swift-corelibs-foundation")
extension NSTextCheckingResult {
open class func orthographyCheckingResult(range: NSRange, orthography: NSOrthography) -> NSTextCheckingResult {
NSUnsupported()
}

open class func spellCheckingResult(range: NSRange) -> NSTextCheckingResult {
NSUnsupported()
}

open class func grammarCheckingResult(range: NSRange, details: [[String : Any]]) -> NSTextCheckingResult {
NSUnsupported()
}

open class func dateCheckingResult(range: NSRange, date: Date) -> NSTextCheckingResult {
NSUnsupported()
}

open class func dateCheckingResult(range: NSRange, date: Date, timeZone: TimeZone, duration: TimeInterval) -> NSTextCheckingResult {
NSUnsupported()
}

open class func addressCheckingResult(range: NSRange, components: [NSTextCheckingKey : String]) -> NSTextCheckingResult {
NSUnsupported()
}

open class func linkCheckingResult(range: NSRange, url: URL) -> NSTextCheckingResult {
NSUnsupported()
}

open class func quoteCheckingResult(range: NSRange, replacementString: String) -> NSTextCheckingResult {
NSUnsupported()
}

open class func dashCheckingResult(range: NSRange, replacementString: String) -> NSTextCheckingResult {
NSUnsupported()
}

open class func replacementCheckingResult(range: NSRange, replacementString: String) -> NSTextCheckingResult {
NSUnsupported()
}

open class func correctionCheckingResult(range: NSRange, replacementString: String, alternativeStrings: [String]) -> NSTextCheckingResult {
NSUnsupported()
}

open class func phoneNumberCheckingResult(range: NSRange, phoneNumber: String) -> NSTextCheckingResult {
NSUnsupported()
}

open class func transitInformationCheckingResult(range: NSRange, components: [NSTextCheckingKey : String]) -> NSTextCheckingResult {
NSUnsupported()
}
}

@available(*, deprecated, message: "NSOrtography is not available in swift-corelibs-foundation")
open class NSOrthography: NSObject, NSCopying, NSSecureCoding {
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open class func defaultOrtography(forLanguage: String) -> Self {
NSUnsupported()
}

@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
public init(dominantScript: String, languageMap: [String: [String]]) {
NSUnsupported()
}

public func copy(with zone: NSZone?) -> Any {
NSUnsupported()
}

open class var supportsSecureCoding: Bool { NSUnsupported() }

public func encode(with aCoder: NSCoder) {
NSUnsupported()
}

public required init?(coder aDecoder: NSCoder) {
NSUnsupported()
}

@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open var languageMap: [String: [String]] { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open var dominantLanguage: String { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open var dominantScript: String { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open func dominantLanguage(forScript: String) -> String? { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open func language(forScript: String) -> [String]? { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open var alLScripts: [String] { NSUnsupported() }
@available(*, unavailable, message: "NSOrtography is not available in swift-corelibs-foundation")
open var allLanguages: [String] { NSUnsupported() }
}
Loading