From 315b2de2a0be2150d5cb2d1d8e555c5b4d53313a Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 10 Jun 2020 10:30:19 +0700 Subject: [PATCH] change(ios/engine): usage of #3220's new typings, beter docs --- .../Classes/KeyboardKeymanPackage.swift | 2 +- .../KeymanEngine/Classes/KeymanPackage.swift | 60 +++++++++++++------ .../Classes/LexicalModelKeymanPackage.swift | 2 +- .../KMEI/KeymanEngine/Classes/Manager.swift | 4 +- .../ResourceFileManager.swift | 18 ++---- .../FileManagementTests.swift | 45 ++++---------- .../KeymanPackageTests.swift | 22 +++++-- 7 files changed, 79 insertions(+), 74 deletions(-) diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift index c33e344f40..71993234b7 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeyboardKeymanPackage.swift @@ -11,7 +11,7 @@ import Foundation public class KeyboardKeymanPackage : TypedKeymanPackage { internal var keyboards: [KMPKeyboard]! - override init(metadata: KMPMetadata, folder: URL) { + override internal init(metadata: KMPMetadata, folder: URL) { super.init(metadata: metadata, folder: folder) self.keyboards = [] diff --git a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift index d6bd0552bc..dce8a0d192 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/KeymanPackage.swift @@ -18,12 +18,15 @@ public enum KMPError : String, Error { case resourceNotInPackage = "Resource cannot be found in specified package" } +/** + * Common base class for the different KeymanPackage types. + */ public class KeymanPackage { static private let kmpFile = "kmp.json" public let sourceFolder: URL internal let metadata: KMPMetadata - init(metadata: KMPMetadata, folder: URL) { + internal init(metadata: KMPMetadata, folder: URL) { sourceFolder = folder self.metadata = metadata } @@ -32,6 +35,21 @@ public class KeymanPackage { return metadata.packageType == .Keyboard } + /** + * Returns the type of LanguageResource contained within the parsed Package. + */ + public func resourceType() -> LanguageResourceType { + switch metadata.packageType { + case .Keyboard: + return .keyboard + case .LexicalModel: + return .lexicalModel + default: + // See KeymanPackage.parse below; an error is thrown there for case .Unsupported. + fatalError("KeymanPackage.parse failed to block construction of unsupported KeymanPackage instance") + } + } + // to be overridden by subclasses public func defaultInfoHtml() -> String { return "base class!" @@ -68,7 +86,13 @@ public class KeymanPackage { public var installableResourceSets: [[AnyLanguageResource]] { fatalError("abstract base method went unimplemented by derived class") } - + + /** + * Parses a decompressed KMP's metadata file, producing the typed KeymanPackage instance corresponding to it. + * + * Typecast the return value to either KeyboardKeymanPackage or LexicalModelKeymanPackage for richly-typed + * information about the package's contents. + */ static public func parse(_ folder: URL) -> KeymanPackage? { do { var path = folder @@ -117,22 +141,6 @@ public class KeymanPackage { } }) } - - public func findMatch(_ resource: AnyLanguageResource) -> AnyLanguageResource? { - let matchesFound: [AnyLanguageResource] = self.installableResourceSets.compactMap { set in - let setMatches: [AnyLanguageResource] = set.compactMap { other in - if resource.id == other.id && resource.languageID == other.languageID { - return other - } else { - return nil - } - } - - return setMatches.count > 0 ? setMatches[0] : nil - } - - return matchesFound.count > 0 ? matchesFound[0] : nil - } } public class TypedKeymanPackage: KeymanPackage { @@ -150,4 +158,20 @@ public class TypedKeymanPackage: Keyman public override var installableResourceSets: [[AnyLanguageResource]] { return installables } + + public func findResource(withID fullID: TypedLanguageResource.FullID) -> TypedLanguageResource? { + let matchesFound: [TypedLanguageResource] = self.installables.compactMap { set in + let setMatches: [TypedLanguageResource] = set.compactMap { other in + if fullID.id == other.id && fullID.languageID == other.languageID { + return other + } else { + return nil + } + } + + return setMatches.count > 0 ? setMatches[0] : nil + } + + return matchesFound.count > 0 ? matchesFound[0] : nil + } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift index 44ea0d87d9..9936b21ad6 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/LexicalModelKeymanPackage.swift @@ -11,7 +11,7 @@ import Foundation public class LexicalModelKeymanPackage : TypedKeymanPackage { internal var models : [KMPLexicalModel]! - override init(metadata: KMPMetadata, folder: URL) { + override internal init(metadata: KMPMetadata, folder: URL) { super.init(metadata: metadata, folder: folder) self.models = [] diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift index c26f21c4b1..9d406e80de 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Manager.swift @@ -619,7 +619,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { for resourceSet in kmp.installables { for resource in resourceSet { - try ResourceFileManager.shared.install(resource, from: kmp) + try ResourceFileManager.shared.install(resourceWithID: resource.fullID, from: kmp) // Install the keyboard for only the first language pairing defined in the package. break } @@ -634,7 +634,7 @@ public class Manager: NSObject, UIGestureRecognizerDelegate { try kmp.installables.forEach { resourceSet in try resourceSet.forEach { resource in - try ResourceFileManager.shared.install(resource, from: kmp) + try ResourceFileManager.shared.install(resourceWithID: resource.fullID, from: kmp) } } } diff --git a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift index 7b9356409c..d8d76085ee 100644 --- a/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift +++ b/ios/engine/KMEI/KeymanEngine/Classes/Resource Management/ResourceFileManager.swift @@ -179,23 +179,15 @@ public class ResourceFileManager { } } - public func install(_ resourceToMatch: AnyLanguageResource, from package: KeymanPackage, usePackageDefinition: Bool = true) throws { + public func install> ( + resourceWithID fullID: ResourceType.FullID, + from package: PackageType) throws { - var res: AnyLanguageResource - guard let r = package.findMatch(resourceToMatch) else { + guard let resource = package.findResource(withID: fullID) else { throw KMPError.resourceNotInPackage } - if(usePackageDefinition) { - res = r - } else { - // In case a KeymanEngine user wants a different definition than is in the source KMP. - res = resourceToMatch - } - - // Now to lock down the reference, using 'let' to make it read-only. - let resource = res - // (source, destination) var installableFiles: [(String, URL)] = [(resource.sourceFilename, Storage.active.resourceURL(for: resource)!)] let installableFonts: [(String, URL)] = resource.fonts.map { font in diff --git a/ios/engine/KMEI/KeymanEngineTests/FileManagementTests.swift b/ios/engine/KMEI/KeymanEngineTests/FileManagementTests.swift index 7d4afe5809..96bc4f6dfc 100644 --- a/ios/engine/KMEI/KeymanEngineTests/FileManagementTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/FileManagementTests.swift @@ -75,10 +75,13 @@ class FileManagementTests: XCTestCase { ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.khmerAngkorKMP) { kmp, error in XCTAssertNotNil(kmp, "Failed to prepare KMP for installation") XCTAssertNil(error, "Error occurred while preparing KMP for installation") - XCTAssertNotNil(kmp as? KeyboardKeymanPackage, "KMP resource type improperly recognized - expected a keyboard package!") + guard let kmp = kmp as? KeyboardKeymanPackage else { + XCTFail("KMP resource type improperly recognized - expected a keyboard package!") + return + } do { - try ResourceFileManager.shared.install(TestUtils.Keyboards.khmer_angkor, from: kmp!) + try ResourceFileManager.shared.install(resourceWithID: TestUtils.Keyboards.khmer_angkor.fullID, from: kmp) } catch { XCTFail("Unexpected error during KeyboardPackage install") } @@ -100,45 +103,17 @@ class FileManagementTests: XCTestCase { } } - func testInstallKeyboardFromPackageCustomDefinition() throws { - // Modified installation - KeymanEngine user supplies external/literal definition for LanguageResource. - // Separate test method b/c "tearDown()" cleans out installation artifacts from the other version. - ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.khmerAngkorKMP) { kmp, error in - XCTAssertNotNil(kmp, "Failed to prepare KMP for installation") - XCTAssertNil(error, "Error occurred while preparing KMP for installation") - XCTAssertNotNil(kmp as? KeyboardKeymanPackage, "KMP resource type improperly recognized - expected a keyboard package!") - - do { - try ResourceFileManager.shared.install(TestUtils.Keyboards.khmer_angkor, from: kmp!, usePackageDefinition: false) - } catch { - XCTFail("Unexpected error during KeyboardPackage install") - } - - let installURL = Storage.active.keyboardURL(forID: "khmer_angkor", version: "1.0.6") - - XCTAssertTrue(FileManager.default.fileExists(atPath: installURL.path), - "Could not find installed keyboard file") - - let keyboards = Storage.active.userDefaults.userKeyboards! - - XCTAssertEqual(keyboards.count, 1, "Unexpected number of keyboards were installed") - XCTAssertEqual(keyboards[0].id, "khmer_angkor", "Installed keyboard ID mismatch") - - // While the KMP's version of the specified InstallableKeyboard does specify Fonts, - // the literal-based testing version does NOT. Since in this test, we're installing from the predefined, test-copy instance, we expect NOT to see the font from this test's install! - let fontURL = Storage.active.fontURL(forKeyboardID: "khmer_angkor", filename: "Mondulkiri-R.ttf") - XCTAssertFalse(FileManager.default.fileExists(atPath: fontURL.path)) - } - } - func testInstallLexicalModelFromPackage() { ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) { kmp, error in XCTAssertNotNil(kmp, "Failed to prepare KMP for installation") XCTAssertNil(error, "Error occurred while preparing KMP for installation") - XCTAssertNotNil(kmp as? LexicalModelKeymanPackage, "KMP resource type improperly recognized - expected a lexical model package!") + guard let kmp = kmp as? LexicalModelKeymanPackage else { + XCTFail("KMP resource type improperly recognized - expected a lexical model package!") + return + } do { - try ResourceFileManager.shared.install(TestUtils.LexicalModels.mtnt, from: kmp!) + try ResourceFileManager.shared.install(resourceWithID: TestUtils.LexicalModels.mtnt.fullID, from: kmp) } catch { XCTFail("Unexpected error during LexicalModelPackage install") } diff --git a/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift b/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift index 3c088038ef..3295eb0849 100644 --- a/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift +++ b/ios/engine/KMEI/KeymanEngineTests/KeymanPackageTests.swift @@ -77,13 +77,27 @@ class KeymanPackageTests: XCTestCase { func testPackageFindResourceMatch() { ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.Keyboards.khmerAngkorKMP) { kmp, _ in - XCTAssertNotNil(kmp!.findMatch(TestUtils.Keyboards.khmer_angkor)) - XCTAssertNil(kmp!.findMatch(TestUtils.LexicalModels.mtnt)) + guard let kmp = kmp as? KeyboardKeymanPackage else { + XCTFail("Incorrect package type loaded for test") + return + } + XCTAssertNotNil(kmp.findResource(withID: TestUtils.Keyboards.khmer_angkor.fullID)) + // This keyboard's not in the specified testing package. + XCTAssertNil(kmp.findResource(withID: TestUtils.Keyboards.khmer10.fullID)) + + // Thanks to our package typing hierarchy, it's impossible to even TRY finding + // a FullLexicalModelID within a KeyboardKeymanPackage! } ResourceFileManager.shared.prepareKMPInstall(from: TestUtils.LexicalModels.mtntKMP) { kmp, _ in - XCTAssertNotNil(kmp!.findMatch(TestUtils.LexicalModels.mtnt)) - XCTAssertNil(kmp!.findMatch(TestUtils.Keyboards.khmer_angkor)) + guard let kmp = kmp as? LexicalModelKeymanPackage else { + XCTFail("Incorrect package type loaded for test") + return + } + XCTAssertNotNil(kmp.findResource(withID: TestUtils.LexicalModels.mtnt.fullID)) + + // Thanks to our package typing hierarchy, it's impossible to even TRY finding + // a FullKeyboardID within a LexicalModelKeymanPackage! } } }