/* * Copyright 1999-2101 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Created by zhouzhuo on 9/20/16. // import Foundation public typealias CustomMappingKeyValueTuple = (Int, MappingPropertyHandler) struct MappingPath { var segments: [String] static func buildFrom(rawPath: String) -> MappingPath { let regex = try! NSRegularExpression(pattern: "(? Any? { var currentDict: [String: Any]? = self var lastValue: Any? path.segments.forEach { (segment) in lastValue = currentDict?[segment] currentDict = currentDict?[segment] as? [String: Any] } return lastValue } } public class MappingPropertyHandler { var mappingPaths: [MappingPath]? var assignmentClosure: ((Any?) -> (Any?))? var takeValueClosure: ((Any?) -> (Any?))? public init(rawPaths: [String]?, assignmentClosure: ((Any?) -> (Any?))?, takeValueClosure: ((Any?) -> (Any?))?) { let mappingPaths = rawPaths?.map({ (rawPath) -> MappingPath in if HandyJSONConfiguration.deserializeOptions.contains(.caseInsensitive) { return MappingPath.buildFrom(rawPath: rawPath.lowercased()) } return MappingPath.buildFrom(rawPath: rawPath) }).filter({ (mappingPath) -> Bool in return mappingPath.segments.count > 0 }) if let count = mappingPaths?.count, count > 0 { self.mappingPaths = mappingPaths } self.assignmentClosure = assignmentClosure self.takeValueClosure = takeValueClosure } } public class HelpingMapper { private var mappingHandlers = [Int: MappingPropertyHandler]() private var excludeProperties = [Int]() internal func getMappingHandler(key: Int) -> MappingPropertyHandler? { return self.mappingHandlers[key] } internal func propertyExcluded(key: Int) -> Bool { return self.excludeProperties.contains(key) } public func specify(property: inout T, name: String) { self.specify(property: &property, name: name, converter: nil) } public func specify(property: inout T, converter: @escaping (String) -> T) { self.specify(property: &property, name: nil, converter: converter) } public func specify(property: inout T, name: String?, converter: ((String) -> T)?) { let pointer = withUnsafePointer(to: &property, { return $0 }) let key = Int(bitPattern: pointer) let names = (name == nil ? nil : [name!]) if let _converter = converter { let assignmentClosure = { (jsonValue: Any?) -> Any? in if let _value = jsonValue{ if let object = _value as? NSObject { if let str = String.transform(from: object){ return _converter(str) } } } return nil } self.mappingHandlers[key] = MappingPropertyHandler(rawPaths: names, assignmentClosure: assignmentClosure, takeValueClosure: nil) } else { self.mappingHandlers[key] = MappingPropertyHandler(rawPaths: names, assignmentClosure: nil, takeValueClosure: nil) } } public func exclude(property: inout T) { self._exclude(property: &property) } fileprivate func addCustomMapping(key: Int, mappingInfo: MappingPropertyHandler) { self.mappingHandlers[key] = mappingInfo } fileprivate func _exclude(property: inout T) { let pointer = withUnsafePointer(to: &property, { return $0 }) self.excludeProperties.append(Int(bitPattern: pointer)) } } infix operator <-- : LogicalConjunctionPrecedence public func <-- (property: inout T, name: String) -> CustomMappingKeyValueTuple { return property <-- [name] } public func <-- (property: inout T, names: [String]) -> CustomMappingKeyValueTuple { let pointer = withUnsafePointer(to: &property, { return $0 }) let key = Int(bitPattern: pointer) return (key, MappingPropertyHandler(rawPaths: names, assignmentClosure: nil, takeValueClosure: nil)) } // MARK: non-optional properties public func <-- (property: inout Transform.Object, transformer: Transform) -> CustomMappingKeyValueTuple { return property <-- (nil, transformer) } public func <-- (property: inout Transform.Object, transformer: (String?, Transform?)) -> CustomMappingKeyValueTuple { let names = (transformer.0 == nil ? [] : [transformer.0!]) return property <-- (names, transformer.1) } public func <-- (property: inout Transform.Object, transformer: ([String], Transform?)) -> CustomMappingKeyValueTuple { let pointer = withUnsafePointer(to: &property, { return $0 }) let key = Int(bitPattern: pointer) let assignmentClosure = { (jsonValue: Any?) -> Transform.Object? in return transformer.1?.transformFromJSON(jsonValue) } let takeValueClosure = { (objectValue: Any?) -> Any? in if let _value = objectValue as? Transform.Object { return transformer.1?.transformToJSON(_value) as Any } return nil } return (key, MappingPropertyHandler(rawPaths: transformer.0, assignmentClosure: assignmentClosure, takeValueClosure: takeValueClosure)) } // MARK: optional properties public func <-- (property: inout Transform.Object?, transformer: Transform) -> CustomMappingKeyValueTuple { return property <-- (nil, transformer) } public func <-- (property: inout Transform.Object?, transformer: (String?, Transform?)) -> CustomMappingKeyValueTuple { let names = (transformer.0 == nil ? [] : [transformer.0!]) return property <-- (names, transformer.1) } public func <-- (property: inout Transform.Object?, transformer: ([String], Transform?)) -> CustomMappingKeyValueTuple { let pointer = withUnsafePointer(to: &property, { return $0 }) let key = Int(bitPattern: pointer) let assignmentClosure = { (jsonValue: Any?) -> Any? in return transformer.1?.transformFromJSON(jsonValue) } let takeValueClosure = { (objectValue: Any?) -> Any? in if let _value = objectValue as? Transform.Object { return transformer.1?.transformToJSON(_value) as Any } return nil } return (key, MappingPropertyHandler(rawPaths: transformer.0, assignmentClosure: assignmentClosure, takeValueClosure: takeValueClosure)) } infix operator <<< : AssignmentPrecedence public func <<< (mapper: HelpingMapper, mapping: CustomMappingKeyValueTuple) { mapper.addCustomMapping(key: mapping.0, mappingInfo: mapping.1) } public func <<< (mapper: HelpingMapper, mappings: [CustomMappingKeyValueTuple]) { mappings.forEach { (mapping) in mapper.addCustomMapping(key: mapping.0, mappingInfo: mapping.1) } } infix operator >>> : AssignmentPrecedence public func >>> (mapper: HelpingMapper, property: inout T) { mapper._exclude(property: &property) }