X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=KCD%2FJSONMapper.swift;h=5ab7d34627b65b288d1954c3838127654b3afbf4;hb=e341ef2774c1d7a27d9eb60edab905f1adc510a3;hp=b19fa1f3ab6ebc14d56df6191d3ae3160cc472ab;hpb=33321d136a316f4be982544c32144de187c84fa1;p=kcd%2FKCD.git diff --git a/KCD/JSONMapper.swift b/KCD/JSONMapper.swift index b19fa1f3..5ab7d346 100644 --- a/KCD/JSONMapper.swift +++ b/KCD/JSONMapper.swift @@ -8,32 +8,33 @@ import Cocoa import SwiftyJSON +import Doutaku -struct MappingConfiguration { - let entity: Entity +struct MappingConfiguration { + + let entity: T.Type let dataKeys: [String] - let primaryKey: String - let compositPrimaryKeys: [String]? + let primaryKeys: [String] let editorStore: CoreDataAccessor let ignoreKeys: [String] - init(entity: Entity, + init(entity: T.Type, dataKeys: [String] = ["api_data"], - primaryKey: String = "id", - compositPrimaryKeys: [String]? = nil, + primaryKeys: [String] = ["id"], editorStore: CoreDataAccessor, ignoreKeys: [String] = []) { + self.entity = entity self.dataKeys = dataKeys - self.primaryKey = primaryKey - self.compositPrimaryKeys = compositPrimaryKeys + self.primaryKeys = primaryKeys self.editorStore = editorStore self.ignoreKeys = ignoreKeys } } protocol JSONMapper { - associatedtype ObjectType: NSManagedObject + + associatedtype ObjectType: Entity init(_ apiResponse: APIResponse) @@ -48,97 +49,178 @@ protocol JSONMapper { } extension String { + // delete api_ prefix. func keyByDeletingPrefix() -> String { - if self.characters.count < 5 { return self } - let s = self.index(self.startIndex, offsetBy: 4) - return self[s.. Bool { - if lhs == nil, rhs == nil { return true } - if let lhs = lhs, let rhs = rhs { return lhs.isEqual(rhs) } + + if lhs == nil, rhs == nil { + + return true + } + if let lhs = lhs, let rhs = rhs { + + return lhs.isEqual(rhs) + } + return false } + func setValueIfNeeded(_ value: JSON, to object: ObjectType, forKey key: String) { + var validValue = value.object as AnyObject? do { + try object.validateValue(&validValue, forKey: key) + } catch { + return } let old = object.value(forKey: key) if !isEqual(old as AnyObject?, validValue) { - object.willChangeValue(forKey: key) + object.setValue(validValue, forKey: key) - object.didChangeValue(forKey: key) } } func registerElement(_ element: JSON, to object: ObjectType) { + beginRegister(object) - element.forEach { (key: String, value: JSON) in - if configuration.ignoreKeys.contains(key) { return } - if handleExtraValue(value, forKey: key, to: object) { return } + element.forEach { (key, value) in + + if configuration.ignoreKeys.contains(key) { + + return + } + if handleExtraValue(value, forKey: key, to: object) { + + return + } + switch value.type { case .array: value.array?.enumerated().forEach { + let newKey = "\(key)_\($0.offset)" setValueIfNeeded($0.element, to: object, forKey: newKey) } + case .dictionary: value.forEach { (subKey: String, subValue) in + let newKey = "\(key)_D_\(subKey.keyByDeletingPrefix())" setValueIfNeeded(subValue, to: object, forKey: newKey) } + default: setValueIfNeeded(value, to: object, forKey: key) + } } } + private var sortDescriptors: [NSSortDescriptor] { - let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey] - return keys.map { NSSortDescriptor(key: $0, ascending: true) } + + return configuration.primaryKeys.map { NSSortDescriptor(key: $0, ascending: true) } } + private func objectSearch(_ objects: [ObjectType], _ element: JSON) -> ObjectType? { - let keys = configuration.compositPrimaryKeys ?? [configuration.primaryKey] - let keyPiar = keys.map { (key: $0, apiKey: "api_\($0)") } + + let keyPiar = configuration.primaryKeys.map { (key: $0, apiKey: "api_\($0)") } + return objects.binarySearch { + + // TODO: replace to forEach for piar in keyPiar { - guard let v1 = $0.value(forKey: piar.key) - else { return .orderedAscending } - if element[piar.apiKey].type == .null { return .orderedDescending } + + guard let v1 = $0.value(forKey: piar.key) else { + + return .orderedAscending + } + + if element[piar.apiKey].type == .null { + + return .orderedDescending + } + let v2 = element[piar.apiKey].object + return (v1 as AnyObject).compare(v2) } + return .orderedDescending } } - func commit() { + + private func sortedObjects(_ entity: ResultType.Type) -> [ResultType] { + + let store = configuration.editorStore + + guard let objects = try? store.objects(of: configuration.entity) else { + + Logger.shared.log("Can not get entity named \(configuration.entity)") + + return [] + } + + return (objects as NSArray).sortedArray(using: sortDescriptors) as? [ResultType] ?? [] + } + private func commintInContext() { + let store = configuration.editorStore - guard let objects = try? store.objects(with: configuration.entity, sortDescriptors: sortDescriptors) - else { return print("Can not get entity named \(configuration.entity.name)") } + let objects = sortedObjects(configuration.entity) + let list = (data.type == .array ? data.arrayValue : [data]) list.forEach { + if let object = objectSearch(objects, $0) { + registerElement($0, to: object) + } else if let new = store.insertNewObject(for: configuration.entity) { + registerElement($0, to: new) + } else { - fatalError("Can not get entity named \(configuration.entity.name)") + + fatalError("Can not get entity named \(configuration.entity)") } } + finishOperating() - store.saveActionCore() + store.save() + } + + func commit() { + + configuration.editorStore + .sync { + + self.commintInContext() + } } func beginRegister(_ object: ObjectType) {} + func handleExtraValue(_ value: JSON, forKey key: String, to object: ObjectType) -> Bool { + return false } + func finishOperating() {} }