blob: 56b155e8a0d58b534e6eefb485d13678d872a42b [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
// Predicates wrap some combination of expressions and operators and when evaluated return a BOOL.
// NSPredicates are supported only in a limited form in swift-corelibs-foundation:
// - We only support predicates that do not use strings. Metadata queries and format strings are not supported in swift-corelibs-foundation.
// - We do not support archiving predicates. NSPredicate does not conform to NSSecureCoding in swift-corelibs-foundation.
// We support the following features for compatibility with XCTest:
// - Predicates that are always true or false.
// - Predicates built using a closure.
// - Compound predicates that include the two kinds above. Use NSCompoundPredicate to construct these.
open class NSPredicate : NSObject, NSCopying {
private enum PredicateKind {
case boolean(Bool)
case block((Any?, [String : Any]?) -> Bool)
}
private let kind: PredicateKind
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
switch kind {
case .boolean(let bool):
return NSPredicate(value: bool)
case .block(let block):
return NSPredicate(block: block)
}
}
open override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? NSPredicate else { return false }
if other === self {
return true
} else {
switch (other.kind, self.kind) {
case (.boolean(let otherBool), .boolean(let selfBool)):
return otherBool == selfBool
default:
// NSBlockPredicate returns false even for copy
return false
}
}
}
@available(*, unavailable, message: "Predicate strings and key-value coding are not supported in swift-corelibs-foundation. Use a closure instead if possible.", renamed: "init(block:)")
public init(format predicateFormat: String, argumentArray arguments: [Any]?) { NSUnsupported() }
@available(*, unavailable, message: "Predicate strings and key-value coding are not supported in swift-corelibs-foundation. Use a closure instead if possible.", renamed: "init(block:)")
public init(format predicateFormat: String, arguments argList: CVaListPointer) { NSUnsupported() }
@available(*, unavailable, message: "Spotlight queries are not supported by swift-corelibs-foundation")
public init?(fromMetadataQueryString queryString: String) { NSUnsupported() }
public init(value: Bool) {
kind = .boolean(value)
super.init()
} // return predicates that always evaluate to true/false
public init(block: @escaping (Any?, [String : Any]?) -> Bool) {
kind = .block(block)
super.init()
}
@available(*, deprecated, message: "Predicate strings are not supported in swift-corelibs-foundation. The string returned by this method is not useful outside of this process and should not be serialized.")
open var predicateFormat: String {
switch self.kind {
case .boolean(let value):
return value ? "TRUEPREDICATE" : "FALSEPREDICATE"
case .block:
return "BLOCKPREDICATE"
}
}
@available(*, unavailable, message: "Predicates with substitution variables are not supported in swift-corelibs-foundation.")
open func withSubstitutionVariables(_ variables: [String : Any]) -> Self { NSUnsupported() } // substitute constant values for variables
open func evaluate(with object: Any?) -> Bool {
return evaluate(with: object, substitutionVariables: nil)
} // evaluate a predicate against a single object
open func evaluate(with object: Any?, substitutionVariables bindings: [String : Any]?) -> Bool {
switch kind {
case let .boolean(value):
return value
case let .block(block):
return block(object, bindings)
}
} // single pass evaluation substituting variables from the bindings dictionary for any variable expressions encountered
@available(*, unavailable, message: "Archived predicates are not supported in swift-corelibs-foundation.")
open func allowEvaluation() { NSUnsupported() } // Force a predicate which was securely decoded to allow evaluation
}
extension NSPredicate {
@available(*, unavailable, message: "Predicate strings and key-value coding are not supported in swift-corelibs-foundation. Use a closure instead if possible.", renamed: "init(block:)")
public convenience init(format predicateFormat: String, _ args: CVarArg...) { NSUnsupported() }
}
extension NSArray {
open func filtered(using predicate: NSPredicate) -> [Any] {
return allObjects.filter({ object in
return predicate.evaluate(with: object)
})
}
}
extension NSMutableArray {
open func filter(using predicate: NSPredicate) {
var indexesToRemove = IndexSet()
for (index, object) in self.enumerated() {
if !predicate.evaluate(with: object) {
indexesToRemove.insert(index)
}
}
self.removeObjects(at: indexesToRemove)
}
}
extension NSSet {
open func filtered(using predicate: NSPredicate) -> Set<AnyHashable> {
let objs = allObjects.filter { (object) -> Bool in
return predicate.evaluate(with: object)
}
return Set(objs.map { $0 as! AnyHashable })
}
}
extension NSMutableSet {
open func filter(using predicate: NSPredicate) {
for object in self {
if !predicate.evaluate(with: object) {
self.remove(object)
}
}
}
}
extension NSOrderedSet {
open func filtered(using predicate: NSPredicate) -> NSOrderedSet {
return NSOrderedSet(array: self.allObjects.filter({ object in
return predicate.evaluate(with: object)
}))
}
}
extension NSMutableOrderedSet {
open func filter(using predicate: NSPredicate) {
var indexesToRemove = IndexSet()
for (index, object) in self.enumerated() {
if !predicate.evaluate(with: object) {
indexesToRemove.insert(index)
}
}
self.removeObjects(at: indexesToRemove)
}
}