blob: b5b4642711aa2305dd21285d43f987e269737abd [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.
open class NSPredicate : NSObject, NSSecureCoding, NSCopying {
private enum PredicateKind {
case boolean(Bool)
case block((Any?, [String : Any]?) -> Bool)
case format(String)
case metadataQuery(String)
}
private let kind: PredicateKind
public static var supportsSecureCoding: Bool {
return true
}
public required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
let encodedBool = aDecoder.decodeBool(forKey: "NS.boolean.value")
self.kind = .boolean(encodedBool)
super.init()
}
open func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
//TODO: store kind key for .boolean, .format, .metadataQuery
switch self.kind {
case .boolean(let value):
aCoder.encode(value, forKey: "NS.boolean.value")
case .block:
preconditionFailure("NSBlockPredicate cannot be encoded or decoded.")
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
switch self.kind {
case .boolean(let value):
return NSPredicate(value: value)
case .block(let block):
return NSPredicate(block: block)
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}
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
case (.format, .format):
NSUnimplemented()
case (.metadataQuery, .metadataQuery):
NSUnimplemented()
default:
// NSBlockPredicate returns false even for copy
return false
}
}
}
// Parse predicateFormat and return an appropriate predicate
public init(format predicateFormat: String, argumentArray arguments: [Any]?) { NSUnimplemented() }
public init(format predicateFormat: String, arguments argList: CVaListPointer) { NSUnimplemented() }
public init?(fromMetadataQueryString queryString: String) { NSUnimplemented() }
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()
}
open var predicateFormat: String {
switch self.kind {
case .boolean(let value):
return value ? "TRUEPREDICATE" : "FALSEPREDICATE"
case .block:
// TODO: Bring NSBlockPredicate's predicateFormat to macOS's Foundation version
// let address = unsafeBitCast(block, to: Int.self)
// return String(format:"BLOCKPREDICATE(%2X)", address)
return "BLOCKPREDICATE"
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}
open func withSubstitutionVariables(_ variables: [String : Any]) -> Self { NSUnimplemented() } // 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 {
if bindings != nil {
NSUnimplemented()
}
switch kind {
case let .boolean(value):
return value
case let .block(block):
return block(object, bindings)
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
} // single pass evaluation substituting variables from the bindings dictionary for any variable expressions encountered
open func allowEvaluation() { NSUnimplemented() } // Force a predicate which was securely decoded to allow evaluation
}
extension NSPredicate {
public convenience init(format predicateFormat: String, _ args: CVarArg...) { NSUnimplemented() }
}
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)
}
}