<commit_before>//
//  SwCD.swift
//  SwCD
//
//  Created by xxxAIRINxxx on 2015/05/01.
//  Copyright (c) 2015 xxxAIRINxxx. All rights reserved.
//

import UIKit
import CoreData

public typealias SaveCompletionHandler = (Bool, NSError?) -> Void

public class SwCD {
    
    // MARK: - Public
    
    public class func setup(modelName: String, dbRootDirPath: NSSearchPathDirectory?, dbDirName: String?, dbName: String?) {
        SwCD.sharedInstance.modelName = modelName
        
        if let _dbRootDirPath = dbRootDirPath {
            SwCD.sharedInstance.dbRootDirPath = NSSearchPathForDirectoriesInDomains(_dbRootDirPath, .UserDomainMask, true)[0]
        }
        
        if let _dbDirName = dbDirName {
            SwCD.sharedInstance.dbDirName = _dbDirName
        }
        
        if let _dbName = dbName {
            SwCD.sharedInstance.dbName = _dbName
        }
    }
    
    // MARK: - Private
    
    private static var sharedInstance = SwCD()
    
    private static let ManagedObjectContextKey : String = "SwCDManagedObjectContextKey"
    
    private var modelName : String?
    private var dbRootDirPath : String = NSSearchPathForDirectoriesInDomains(.ApplicationSupportDirectory, .UserDomainMask, true)[0]
    private var dbDirName : String = "SwCD"
    private var dbName : String = "SwCDDB"
    
    private lazy var mainQueueContext : NSManagedObjectContext = {
        var context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        context.parentContext = self.rootSaveingContext
        context.obtainPermanentIDsBeforeSaving()
        context.registerContextDidSaveNotification()
        return context
        }()
    
    private lazy var rootSaveingContext : NSManagedObjectContext = {
        var context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        context.performBlockAndWait({
            context.persistentStoreCoordinator = self.defaultCoordinator
        })
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        return context
        }()
    
    private lazy var defaultCoordinator : NSPersistentStoreCoordinator = {
        var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.defaultManagedObjectModel)
        
        do {
            try coordinator.addPersistentStoreWithType(
                NSSQLiteStoreType,
                configuration: nil,
                URL: self.storeURL(),
                options: nil)
        } catch let error as NSError {
            // re create
            let isMigrationError = (error.code == NSPersistentStoreIncompatibleVersionHashError) || (error.code == NSMigrationMissingSourceModelError)
            
            if error.domain == NSCocoaErrorDomain && isMigrationError {
                do {
                    try NSFileManager.defaultManager().removeItemAtURL(self.storeURL())
                } catch let error as NSError {
                    print("NSFileManager removeItemAtURL error : " + error.localizedDescription)
                    abort()
                }
                
                do {
                    try coordinator.addPersistentStoreWithType(
                        NSSQLiteStoreType,
                        configuration: nil,
                        URL: self.storeURL(),
                        options: nil)
                } catch let error as NSError {
                    print("create persistentStores error : " + error.localizedDescription)
                    abort()
                }
            }
        }
        
        let persistentStores = coordinator.persistentStores
        if persistentStores.count > 0 && self.defaultPersistentStore == nil {
            self.defaultPersistentStore = persistentStores[0]
        }
        
        return coordinator
        }()
    
    private lazy var defaultManagedObjectModel : NSManagedObjectModel = {
        if self.modelName == nil {
            assert(true, "need set model name")
        }
        
        var modelURL = NSBundle.mainBundle().URLForResource(self.modelName!, withExtension: "momd")
        // need unit test
        if modelURL == nil {
            modelURL = NSBundle(forClass: SwCD.self).URLForResource(self.modelName!, withExtension: "momd")
        }
        var model = NSManagedObjectModel(contentsOfURL: modelURL!)
        return model!
        }()
    
    private var defaultPersistentStore : NSPersistentStore?
    
    // MARK: - FilePath
    
    private func storeURL() -> NSURL {
        let dirPath = self.dbRootDirPath + "/" + self.dbDirName
        self.createDirectoryIfNeeded(dirPath)
        let filePath = (dirPath as NSString).stringByAppendingPathComponent(self.dbName + ".sqlite")
        return NSURL(fileURLWithPath: filePath)
    }
    
    private func createDirectoryIfNeeded(path: String) -> Bool {
        if NSFileManager.defaultManager().fileExistsAtPath(path) == false {
            do {
                try NSFileManager.defaultManager().createDirectoryAtPath(path,
                    withIntermediateDirectories: true,
                    attributes: nil)
                return true
            } catch _ {
                return false
            }
        } else {
            return true
        }
    }
}

// MARK: - NSManagedObjectContext Extensions

public extension NSManagedObjectContext {
    
    class func mainQueueContext() -> NSManagedObjectContext {
        return SwCD.sharedInstance.mainQueueContext
    }
    
    class func rootSaveingContext() -> NSManagedObjectContext {
        return SwCD.sharedInstance.rootSaveingContext
    }
    
    class func contextWithParent(parentContext: NSManagedObjectContext) -> NSManagedObjectContext {
        let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        context.parentContext = parentContext
        
        return context
    }
    
    class func contextForCurrentThread() -> NSManagedObjectContext {
        if NSThread.isMainThread() == true {
            return NSManagedObjectContext.mainQueueContext()
        } else {
            let threads = NSThread.currentThread().threadDictionary
            var threadContext = threads[SwCD.ManagedObjectContextKey] as! NSManagedObjectContext?
            if threadContext == nil {
                threadContext = NSManagedObjectContext.contextWithParent(NSManagedObjectContext.rootSaveingContext())
                threads[SwCD.ManagedObjectContextKey] = threadContext
            }
            return threadContext!
        }
    }
    
    // MARK: - Notification
    
    func registerContextDidSaveNotification() {
        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: "contextWillSave:",
            name: NSManagedObjectContextDidSaveNotification,
            object: self)
    }
    
    func obtainPermanentIDsBeforeSaving() {
        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: "contextWillSave:",
            name: NSManagedObjectContextWillSaveNotification,
            object: NSManagedObjectContext.rootSaveingContext())
    }
    
    func rootContextChanged(notification: NSNotification) {
        if let _context = notification.object as? NSManagedObjectContext {
            if _context != NSManagedObjectContext.rootSaveingContext() {
                return
            }
            
            if NSThread.isMainThread() == false {
                dispatch_async(dispatch_get_main_queue(), {
                    self.rootContextChanged(notification)
                })
            } else {
                NSManagedObjectContext.mainQueueContext().mergeChangesFromContextDidSaveNotification(notification)
            }
        }
    }
    <commit_msg>Updated code for Swift 2.2<commit_after>//
//  SwCD.swift
//  SwCD
//
//  Created by xxxAIRINxxx on 2015/05/01.
//  Copyright (c) 2015 xxxAIRINxxx. All rights reserved.
//

import UIKit
import CoreData

public typealias SaveCompletionHandler = (Bool, NSError?) -> Void

public class SwCD {
    
    // MARK: - Public
    
    public class func setup(modelName: String, dbRootDirPath: NSSearchPathDirectory?, dbDirName: String?, dbName: String?) {
        SwCD.sharedInstance.modelName = modelName
        
        if let _dbRootDirPath = dbRootDirPath {
            SwCD.sharedInstance.dbRootDirPath = NSSearchPathForDirectoriesInDomains(_dbRootDirPath, .UserDomainMask, true)[0]
        }
        
        if let _dbDirName = dbDirName {
            SwCD.sharedInstance.dbDirName = _dbDirName
        }
        
        if let _dbName = dbName {
            SwCD.sharedInstance.dbName = _dbName
        }
    }
    
    // MARK: - Private
    
    private static var sharedInstance = SwCD()
    
    private static let ManagedObjectContextKey : String = "SwCDManagedObjectContextKey"
    
    private var modelName : String?
    private var dbRootDirPath : String = NSSearchPathForDirectoriesInDomains(.ApplicationSupportDirectory, .UserDomainMask, true)[0]
    private var dbDirName : String = "SwCD"
    private var dbName : String = "SwCDDB"
    
    private lazy var mainQueueContext : NSManagedObjectContext = {
        var context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        context.parentContext = self.rootSaveingContext
        context.obtainPermanentIDsBeforeSaving()
        context.registerContextDidSaveNotification()
        return context
        }()
    
    private lazy var rootSaveingContext : NSManagedObjectContext = {
        var context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        context.performBlockAndWait({
            context.persistentStoreCoordinator = self.defaultCoordinator
        })
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        return context
        }()
    
    private lazy var defaultCoordinator : NSPersistentStoreCoordinator = {
        var coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.defaultManagedObjectModel)
        
        do {
            try coordinator.addPersistentStoreWithType(
                NSSQLiteStoreType,
                configuration: nil,
                URL: self.storeURL(),
                options: nil)
        } catch let error as NSError {
            // re create
            let isMigrationError = (error.code == NSPersistentStoreIncompatibleVersionHashError) || (error.code == NSMigrationMissingSourceModelError)
            
            if error.domain == NSCocoaErrorDomain && isMigrationError {
                do {
                    try NSFileManager.defaultManager().removeItemAtURL(self.storeURL())
                } catch let error as NSError {
                    print("NSFileManager removeItemAtURL error : " + error.localizedDescription)
                    abort()
                }
                
                do {
                    try coordinator.addPersistentStoreWithType(
                        NSSQLiteStoreType,
                        configuration: nil,
                        URL: self.storeURL(),
                        options: nil)
                } catch let error as NSError {
                    print("create persistentStores error : " + error.localizedDescription)
                    abort()
                }
            }
        }
        
        let persistentStores = coordinator.persistentStores
        if persistentStores.count > 0 && self.defaultPersistentStore == nil {
            self.defaultPersistentStore = persistentStores[0]
        }
        
        return coordinator
        }()
    
    private lazy var defaultManagedObjectModel : NSManagedObjectModel = {
        if self.modelName == nil {
            assert(true, "need set model name")
        }
        
        var modelURL = NSBundle.mainBundle().URLForResource(self.modelName!, withExtension: "momd")
        // need unit test
        if modelURL == nil {
            modelURL = NSBundle(forClass: SwCD.self).URLForResource(self.modelName!, withExtension: "momd")
        }
        var model = NSManagedObjectModel(contentsOfURL: modelURL!)
        return model!
        }()
    
    private var defaultPersistentStore : NSPersistentStore?
    
    // MARK: - FilePath
    
    private func storeURL() -> NSURL {
        let dirPath = self.dbRootDirPath + "/" + self.dbDirName
        self.createDirectoryIfNeeded(dirPath)
        let filePath = (dirPath as NSString).stringByAppendingPathComponent(self.dbName + ".sqlite")
        return NSURL(fileURLWithPath: filePath)
    }
    
    private func createDirectoryIfNeeded(path: String) -> Bool {
        if NSFileManager.defaultManager().fileExistsAtPath(path) == false {
            do {
                try NSFileManager.defaultManager().createDirectoryAtPath(path,
                    withIntermediateDirectories: true,
                    attributes: nil)
                return true
            } catch _ {
                return false
            }
        } else {
            return true
        }
    }
}

// MARK: - NSManagedObjectContext Extensions

public extension NSManagedObjectContext {
    
    class func mainQueueContext() -> NSManagedObjectContext {
        return SwCD.sharedInstance.mainQueueContext
    }
    
    class func rootSaveingContext() -> NSManagedObjectContext {
        return SwCD.sharedInstance.rootSaveingContext
    }
    
    class func contextWithParent(parentContext: NSManagedObjectContext) -> NSManagedObjectContext {
        let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        context.parentContext = parentContext
        
        return context
    }
    
    class func contextForCurrentThread() -> NSManagedObjectContext {
        if NSThread.isMainThread() == true {
            return NSManagedObjectContext.mainQueueContext()
        } else {
            let threads = NSThread.currentThread().threadDictionary
            var threadContext = threads[SwCD.ManagedObjectContextKey] as! NSManagedObjectContext?
            if threadContext == nil {
                threadContext = NSManagedObjectContext.contextWithParent(NSManagedObjectContext.rootSaveingContext())
                threads[SwCD.ManagedObjectContextKey] = threadContext
            }
            return threadContext!
        }
    }
    
    // MARK: - Notification
    
    func registerContextDidSaveNotification() {
        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: #selector(NSManagedObjectContext.contextWillSave(_:)),
            name: NSManagedObjectContextDidSaveNotification,
            object: self)
    }
    
    func obtainPermanentIDsBeforeSaving() {
        NSNotificationCenter.defaultCenter().addObserver(
            self,
            selector: #selector(NSManagedObjectContext.contextWillSave(_:)),
            name: NSManagedObjectContextWillSaveNotification,
            object: NSManagedObjectContext.rootSaveingContext())
    }
    
    func rootContextChanged(notification: NSNotification) {
        if let _context = notification.object as? NSManagedObjectContext {
            if _context != NSManagedObjectContext.rootSaveingContext() {
                return
            }
            
            if NSThread.isMainThread() == false {
                dispatch_async(dispatch_get_main_queue(), {
                    self.rootContextChanged(notification)
                })
            } else {
                NSManagedObjectContext.mainQueueContext().mergeChangesFromContextDidSaveNotification(notification)
            }
        }
    }
    
{'fraction_non_alphanumeric': 0.071616, 'fraction_numerical': 0.002176, 'mean_word_length': 2.0893633847370503, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7508004', 'n_tokens_mistral': 3975, 'n_tokens_neox': 3810, 'n_words': 786}