<commit_before>//
//  MigrationPlan.swift
//  PersistentStoreMigrationKit
//
//  Created by <NAME> on 26.08.15.
//  Copyright (c) 2015 <NAME>. All rights reserved.
//

import Foundation
import CoreData

public final class MigrationPlan: NSObject {
	private var steps = [MigrationStep]()
	public var stepCount: Int { return steps.count }
	public var isEmpty: Bool { return stepCount == 0 }
	
	private static func modelsInBundles(bundles: [NSBundle]) -> [NSManagedObjectModel] {
		var models = [NSManagedObjectModel]()
		for bundle in bundles {
			if let modelURLs = bundle.URLsForResourcesWithExtension("mom", subdirectory: nil) {
				for modelURL in modelURLs {
					if let model = NSManagedObjectModel(contentsOfURL: modelURL) {
						models.append(model)
					}
				}
			}
			if let modelURLs = bundle.URLsForResourcesWithExtension("momd", subdirectory: nil) {
				for modelBundleURL in modelURLs {
					if let modelBundle = NSBundle(URL: modelBundleURL),
						modelURLs = modelBundle.URLsForResourcesWithExtension("mom", subdirectory: nil)
					{
						for modelURL in modelURLs {
							if let model = NSManagedObjectModel(contentsOfURL: modelURL) {
								models.append(model)
							}
						}
					}
				}
			}
		}
		return models
	}
	
	public init(storeMetadata: [String: AnyObject], destinationModel: NSManagedObjectModel, bundles: [NSBundle]) throws {
		precondition(!bundles.isEmpty, "Bundles must be non-empty.")
		let _ = NSProgress(totalUnitCount: -1)
		if destinationModel.isConfiguration(nil, compatibleWithStoreMetadata: storeMetadata) {
			super.init()
			return
		}
		guard let storeModelVersionHashes = storeMetadata[NSStoreModelVersionHashesKey] as? [String: AnyObject] else {
			super.init()
			throw Error.MissingStoreModelVersionHashes
		}
		var latestModelVersionHashes = storeModelVersionHashes
		let models = self.dynamicType.modelsInBundles(bundles)
		while !(latestModelVersionHashes as NSDictionary).isEqualToDictionary(destinationModel.entityVersionHashesByName) {
			let migrationStepIndex = steps.count
			var stepSourceModel: NSManagedObjectModel!
			for model in models {
				if (model.entityVersionHashesByName as NSDictionary).isEqualToDictionary(latestModelVersionHashes) {
					stepSourceModel = model
				}
			}
			if stepSourceModel == nil {
				super.init()
				throw Error.CouldNotFindSourceModel
			}
			var stepDestinationModel: NSManagedObjectModel!
			var stepMappingModel: NSMappingModel!
			for model in models {
				if let mappingModel = NSMappingModel(fromBundles: bundles, forSourceModel: stepSourceModel, destinationModel: model) {
					stepDestinationModel = model
					stepMappingModel = mappingModel
					break
				}
			}
			if stepDestinationModel == nil ||
				stepMappingModel == nil
			{
				super.init()
				throw Error.CouldNotInferMappingSteps
			}
			latestModelVersionHashes = stepDestinationModel.entityVersionHashesByName
			let migrationStep = MigrationStep(sourceModel: stepSourceModel, destinationModel: stepDestinationModel, mappingModel: stepMappingModel)
			steps.append(migrationStep)
		}
		if steps.isEmpty {
			super.init()
			throw Error.CouldNotInferMappingSteps
		}
		super.init()
	}
	<commit_msg>Add documentation comments to MigrationPlan<commit_after>//
//  MigrationPlan.swift
//  PersistentStoreMigrationKit
//
//  Created by <NAME> on 26.08.15.
//  Copyright (c) 2015 <NAME>. All rights reserved.
//

import Foundation
import CoreData

/// A `MigrationPlan` instance encapsulates the progressive migration from one `NSManagedObjectModel` to another with an arbitrary number of intermediate models.
public final class MigrationPlan: NSObject {
	private var steps = [MigrationStep]()
	/// The number of steps in the plan. Zero, if the plan is empty.
	public var stepCount: Int { return steps.count }
	/// Indicates whether executing the plan will do nothing.
	public var isEmpty: Bool { return stepCount == 0 }
	
	private static func modelsInBundles(bundles: [NSBundle]) -> [NSManagedObjectModel] {
		var models = [NSManagedObjectModel]()
		for bundle in bundles {
			if let modelURLs = bundle.URLsForResourcesWithExtension("mom", subdirectory: nil) {
				for modelURL in modelURLs {
					if let model = NSManagedObjectModel(contentsOfURL: modelURL) {
						models.append(model)
					}
				}
			}
			if let modelURLs = bundle.URLsForResourcesWithExtension("momd", subdirectory: nil) {
				for modelBundleURL in modelURLs {
					if let modelBundle = NSBundle(URL: modelBundleURL),
						modelURLs = modelBundle.URLsForResourcesWithExtension("mom", subdirectory: nil)
					{
						for modelURL in modelURLs {
							if let model = NSManagedObjectModel(contentsOfURL: modelURL) {
								models.append(model)
							}
						}
					}
				}
			}
		}
		return models
	}
	
	/// Devises a migration plan based on the metadata of an existing store, a destination managed object model and a list of bundles to search for intermediate models.
	/// 
	/// If no migration is necessary (i.e. the existing store's metadata is compatible with the destination model), the initialized plan will be empty.
	/// 
	/// - Throws: Throws an error if a migration plan cannot be devised.
	/// 
	/// - Parameters:
	///   - storeMetadata: The metadata of an existing persistent store.
	///   - destinationModel: The model to migrate to.
	///   - bundles: A list of bundles to search for the source model and intermediate models.
	public init(storeMetadata: [String: AnyObject], destinationModel: NSManagedObjectModel, bundles: [NSBundle]) throws {
		precondition(!bundles.isEmpty, "Bundles must be non-empty.")
		let _ = NSProgress(totalUnitCount: -1)
		if destinationModel.isConfiguration(nil, compatibleWithStoreMetadata: storeMetadata) {
			super.init()
			return
		}
		guard let storeModelVersionHashes = storeMetadata[NSStoreModelVersionHashesKey] as? [String: AnyObject] else {
			super.init()
			throw Error.MissingStoreModelVersionHashes
		}
		var latestModelVersionHashes = storeModelVersionHashes
		let models = self.dynamicType.modelsInBundles(bundles)
		while !(latestModelVersionHashes as NSDictionary).isEqualToDictionary(destinationModel.entityVersionHashesByName) {
			let migrationStepIndex = steps.count
			var stepSourceModel: NSManagedObjectModel!
			for model in models {
				if (model.entityVersionHashesByName as NSDictionary).isEqualToDictionary(latestModelVersionHashes) {
					stepSourceModel = model
				}
			}
			if stepSourceModel == nil {
				super.init()
				throw Error.CouldNotFindSourceModel
			}
			var stepDestinationModel: NSManagedObjectModel!
			var stepMappingModel: NSMappingModel!
			for model in models {
				if let mappingModel = NSMappingModel(fromBundles: bundles, forSourceModel: stepSourceModel, destinationModel: model) {
					stepDestinationModel = model
					stepMappingModel = mappingModel
					break
				}
			}
			if stepDestinationModel == nil ||
				stepMappingModel == nil
			{
				super.init()
				throw Error.CouldNotInferMappingSteps
			}
			latestModelVersionHashes = stepDestinationModel.entityVersionHashesByName
			let migrationStep = MigrationStep(sourceModel: stepSourceModel, destinationModel: stepDestinationModel, mappingModel: stepMappingModel)
			steps.append(migrationStep)
		}
		if steps.isEmpty {
			super.init()
			throw Error.CouldNotInferMappingSteps
		}
		super.init()
	}
	
	/// Performs the migration from the persistent store identified by `sourceURL` to `destinationModel`, saving the result in the persistent store identified by `destinationURL`.
	/// The migration is guaranteed to be atomic.
	/// 
	/// Inserts an `NSProgress` instance into the current progress tree.
	/// 
	/// - Parameters:
	///   - sourceURL: Identifies the persistent store to migrate from.
	///   - sourceStoreType: A string constant (such as `NSSQLiteStoreType`) that specifies the source store type.
	///   - destinationURL: Identifies the persistent store to migrate to. May be identical to `sourceURL`.
	///   - destinationStoreType: A string constant (such as `NSSQLiteStoreType`) that specifies the destination store type.
{'fraction_non_alphanumeric': 0.08283582089552238, 'fraction_numerical': 0.0029850746268656717, 'mean_word_length': 3.9240661359461115, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, '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': '3392818', 'n_tokens_mistral': 2642, 'n_tokens_neox': 2232, 'n_words': 652}