OSDN Git Service

Doutaku を 1.0 にアップデート
[kcd/KCD.git] / KCD / JSONMapper.swift
index b19fa1f..5ab7d34 100644 (file)
@@ -8,32 +8,33 @@
 
 import Cocoa
 import SwiftyJSON
+import Doutaku
 
-struct MappingConfiguration<T: NSManagedObject> {
-    let entity: Entity<T>
+struct MappingConfiguration<T: Entity> {
+    
+    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<T>,
+    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..<self.endIndex]
+        
+        if self.count < 5 {
+            
+            return self
+        }
+        
+        return String(self[index(startIndex, offsetBy: 4)...])
     }
 }
 
 extension JSONMapper {
+    
     var data: JSON { return apiResponse.json[configuration.dataKeys] }
     
     private func isEqual(_ lhs: AnyObject?, _ rhs: AnyObject?) -> 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<ResultType: Entity>(_ 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() {}
 }