You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

219 lines
8.4 KiB

/*
* 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: "(?<![\\\\])\\.")
let nsString = rawPath as NSString
let results = regex.matches(in: rawPath, range: NSRange(location: 0, length: nsString.length))
var splitPoints = results.map { $0.range.location }
var curPos = 0
var pathArr = [String]()
splitPoints.append(nsString.length)
splitPoints.forEach({ (point) in
let start = rawPath.index(rawPath.startIndex, offsetBy: curPos)
let end = rawPath.index(rawPath.startIndex, offsetBy: point)
let subPath = String(rawPath[start ..< end]).replacingOccurrences(of: "\\.", with: ".")
if !subPath.isEmpty {
pathArr.append(subPath)
}
curPos = point + 1
})
return MappingPath(segments: pathArr)
}
}
extension Dictionary where Key == String, Value: Any {
func findValueBy(path: MappingPath) -> 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<T>(property: inout T, name: String) {
self.specify(property: &property, name: name, converter: nil)
}
public func specify<T>(property: inout T, converter: @escaping (String) -> T) {
self.specify(property: &property, name: nil, converter: converter)
}
public func specify<T>(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<T>(property: inout T) {
self._exclude(property: &property)
}
fileprivate func addCustomMapping(key: Int, mappingInfo: MappingPropertyHandler) {
self.mappingHandlers[key] = mappingInfo
}
fileprivate func _exclude<T>(property: inout T) {
let pointer = withUnsafePointer(to: &property, { return $0 })
self.excludeProperties.append(Int(bitPattern: pointer))
}
}
infix operator <-- : LogicalConjunctionPrecedence
public func <-- <T>(property: inout T, name: String) -> CustomMappingKeyValueTuple {
return property <-- [name]
}
public func <-- <T>(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 <-- <Transform: TransformType>(property: inout Transform.Object, transformer: Transform) -> CustomMappingKeyValueTuple {
return property <-- (nil, transformer)
}
public func <-- <Transform: TransformType>(property: inout Transform.Object, transformer: (String?, Transform?)) -> CustomMappingKeyValueTuple {
let names = (transformer.0 == nil ? [] : [transformer.0!])
return property <-- (names, transformer.1)
}
public func <-- <Transform: TransformType>(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 <-- <Transform: TransformType>(property: inout Transform.Object?, transformer: Transform) -> CustomMappingKeyValueTuple {
return property <-- (nil, transformer)
}
public func <-- <Transform: TransformType>(property: inout Transform.Object?, transformer: (String?, Transform?)) -> CustomMappingKeyValueTuple {
let names = (transformer.0 == nil ? [] : [transformer.0!])
return property <-- (names, transformer.1)
}
public func <-- <Transform: TransformType>(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 >>> <T> (mapper: HelpingMapper, property: inout T) {
mapper._exclude(property: &property)
}