Merge pull request #3234 from keymanapp/feat/ios/engine/package-ids

feat(ios/engine): KeymanPackage now parses its ID
This commit is contained in:
Joshua Horton 2020-06-12 15:28:08 +07:00 committed by GitHub
commit fdac12885d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 79 additions and 22 deletions

View file

@ -17,6 +17,8 @@ public class KeyboardKeymanPackage : TypedKeymanPackage<InstallableKeyboard> {
if let packagedKeyboards = metadata.keyboards {
for keyboard in packagedKeyboards {
keyboard.packageId = self.id
if(keyboard.isValid && FileManager.default.fileExists(atPath: self.sourceFolder.appendingPathComponent("\(keyboard.keyboardId).js").path)) {
keyboards.append(keyboard)
} else {

View file

@ -24,11 +24,32 @@ public enum KMPError : String, Error {
public class KeymanPackage {
static private let kmpFile = "kmp.json"
public let sourceFolder: URL
public let id: String
internal let metadata: KMPMetadata
internal let isTemp: Bool
internal init(metadata: KMPMetadata, folder: URL) {
sourceFolder = folder
self.metadata = metadata
let folderName = folder.lastPathComponent
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
var nameComponents = folderName.components(separatedBy: ".")
// Have we parsed a temporary extraction site?
// This case arises during package installation.
isTemp = folderName.hasSuffix(".kmp.zip") && folder.path.contains(cacheDirectory.path)
if isTemp {
nameComponents.removeLast() // .zip
nameComponents.removeLast() // .kmp
}
// Lexical model packages use .model.kmp, so we remove the final 'model' bit.
if nameComponents.last == "model" {
nameComponents.removeLast()
}
self.id = nameComponents.joined(separator: ".")
}
public func isKeyboard() -> Bool {

View file

@ -17,6 +17,8 @@ public class LexicalModelKeymanPackage : TypedKeymanPackage<InstallableLexicalMo
if let packagedModels = metadata.lexicalModels {
for model in packagedModels {
model.packageId = self.id
// If completely missing, we set the version to 1.0. Legacy decision from 2005.
model.setNilVersion(to: metadata.info?.version?.description ?? "1.0")
if(model.isValid && FileManager.default.fileExists(atPath: self.sourceFolder.appendingPathComponent("\(model.lexicalModelId).model.js").path)) {

View file

@ -9,8 +9,9 @@
import Foundation
/// Mainly differs from the API `Keyboard` by having an associated language.
public struct InstallableKeyboard: Codable, LanguageResource {
public struct InstallableKeyboard: Codable, KMPInitializableLanguageResource {
public typealias FullID = FullKeyboardID
internal typealias Metadata = KMPKeyboard
// Details what properties are coded and decoded re: serialization.
enum CodingKeys: String, CodingKey {
@ -26,7 +27,7 @@ public struct InstallableKeyboard: Codable, LanguageResource {
}
public private(set) var id: String
public internal(set) var packageID: String? = nil
public private(set) var packageID: String? = nil
public var name: String
public private(set) var lgCode: String
public var languageName: String
@ -95,6 +96,25 @@ public struct InstallableKeyboard: Codable, LanguageResource {
self.isCustom = isCustom
}
internal init?(from metadata: KMPKeyboard, packageID: String, lgCode: String) {
self.id = metadata.id
self.name = metadata.name
self.lgCode = lgCode
let languageMatches = metadata.languages.compactMap { return $0.languageId == lgCode ? $0.name : nil }
guard languageMatches.count == 1 else {
return nil
}
self.languageName = languageMatches[0]
self.version = metadata.version
self.isRTL = metadata.isRTL
self.font = metadata.displayFont
self.oskFont = metadata.oskFont
self.packageID = packageID
self.isCustom = false
}
public var fonts: [Font] {
var fonts: [Font] = []

View file

@ -13,7 +13,7 @@ struct InstallableConstants {
}
/// Mainly differs from the API `LexicalModel` by having an associated language.
public struct InstallableLexicalModel: Codable, LanguageResource {
public struct InstallableLexicalModel: Codable, KMPInitializableLanguageResource {
// Details what properties are coded and decoded re: serialization.
enum CodingKeys: String, CodingKey {
case id
@ -70,6 +70,15 @@ public struct InstallableLexicalModel: Codable, LanguageResource {
self.isCustom = isCustom
}
internal init?(from metadata: KMPLexicalModel, packageID: String, lgCode: String) {
self.id = metadata.id
self.name = metadata.name
self.lgCode = lgCode
self.version = metadata.version!
self.isCustom = false
self.packageID = packageID
}
// Lexical models don't bundle fonts. At least, not yet?
public var fonts: [Font] {
return []

View file

@ -52,3 +52,8 @@ extension LanguageResource {
return typedFullID
}
}
internal protocol KMPInitializableLanguageResource: LanguageResource {
associatedtype Metadata: KMPResource
init?(from metadata: Metadata, packageID: String, lgCode: String)
}

View file

@ -11,6 +11,7 @@ import Foundation
class KMPKeyboard: Codable, KMPResource {
public var name: String
public var keyboardId: String
public var packageId: String?
public var version: String
public var osk: String?
public var font: String?
@ -32,6 +33,7 @@ class KMPKeyboard: Codable, KMPResource {
internal required init?(from keyboard: InstallableKeyboard) {
self.name = keyboard.name
self.keyboardId = keyboard.id
self.packageId = keyboard.packageID ?? keyboard.id
self.version = keyboard.version
self.isRTL = keyboard.isRTL
@ -74,15 +76,7 @@ class KMPKeyboard: Codable, KMPResource {
var installableKeyboards : [InstallableKeyboard] = []
for language in self.languages {
let keyboard = InstallableKeyboard(id: keyboardId, name: name,
languageID: language.languageId,
languageName: language.name,
version: version,
isRTL: isRTL,
font: displayFont,
oskFont: oskFont,
isCustom: true) //update this based on adhoc vs api
let keyboard = InstallableKeyboard(from: self, packageID: packageId!, lgCode: language.languageId)!
installableKeyboards.append( keyboard )
}

View file

@ -11,6 +11,7 @@ import Foundation
class KMPLexicalModel: Codable, KMPResource {
public var name: String
public var lexicalModelId: String
public var packageId: String?
public var version: String?
public var isRTL: Bool = false
public var languages: [KMPLanguage]
@ -28,6 +29,7 @@ class KMPLexicalModel: Codable, KMPResource {
self.name = lexicalModel.name
self.lexicalModelId = lexicalModel.id
self.version = lexicalModel.version
self.packageId = lexicalModel.packageID ?? lexicalModel.id
// InstallableLexicalModel doesn't store the language name, so we use the id as a fill-in.
// The 'name' part isn't used for matching, anyway.
@ -74,11 +76,7 @@ class KMPLexicalModel: Codable, KMPResource {
var installableLexicalModels : [InstallableLexicalModel] = []
for language in self.languages {
let model = InstallableLexicalModel(id: lexicalModelId, name: name,
languageID: language.languageId,
version: version ?? "",
isCustom: true) //update this based on adhoc vs api
let model = InstallableLexicalModel(from: self, packageID: packageId!, lgCode: language.languageId)!
installableLexicalModels.append( model )
}

View file

@ -205,6 +205,7 @@ class KMPJSONTests: XCTestCase {
// Three models are listed; we choose the second variant since it uses a different language
// code than specified under TestUtils.LexicalModels.mtnt.
kmp_mtnt.packageId = "nrc.en.mtnt" // We didn't load the full package, so the id's unset.
let constructed_mtnt: KMPLexicalModel = KMPLexicalModel(from: kmp_mtnt.installableResources[1])!
XCTAssertFalse(constructed_mtnt.hasMatchingMetadata(for: TestUtils.LexicalModels.mtnt))

View file

@ -27,7 +27,7 @@ class KeymanPackageTests: XCTestCase {
XCTAssertNotNil(kmp as? KeyboardKeymanPackage, "Keyboard KMP test extraction did not yield a keyboard package!")
XCTAssertTrue(kmp.isKeyboard(), "Keyboard KMP test extraction did not yield a keyboard package!")
XCTAssertEqual(kmp.id, "khmer_angkor", "Incorrect package ID")
// extracted ok, test kmp
XCTAssert(kmp.sourceFolder == destinationFolderURL,
"The KMP's reported 'source folder' should match the specified destination folder")
@ -41,10 +41,10 @@ class KeymanPackageTests: XCTestCase {
func testLexicalModelPackageExtraction() throws {
let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let mtntZip = cacheDirectory.appendingPathComponent("mtnt.zip")
let mtntZip = cacheDirectory.appendingPathComponent("nrc.en.mtnt.zip")
try FileManager.default.copyItem(at: TestUtils.LexicalModels.mtntKMP, to: mtntZip)
let destinationFolderURL = cacheDirectory.appendingPathComponent("mtnt.model")
let destinationFolderURL = cacheDirectory.appendingPathComponent("nrc.en.mtnt.model")
// Requires that the source file is already .zip, not .kmp. It's a ZipUtils limitation.
do {
@ -59,6 +59,7 @@ class KeymanPackageTests: XCTestCase {
XCTAssertNotNil(kmp as? LexicalModelKeymanPackage, "Lexical model KMP test extraction yielded a keyboard package!")
XCTAssertTrue(!kmp.isKeyboard(), "Lexical model KMP test extraction yielded a keyboard package!")
XCTAssertEqual(kmp.id, "nrc.en.mtnt")
// extracted ok, test kmp
XCTAssert(kmp.sourceFolder == destinationFolderURL,
@ -76,7 +77,9 @@ class KeymanPackageTests: XCTestCase {
XCTFail("Incorrect package type loaded for test")
return
}
XCTAssertNotNil(kmp1.findResource(withID: TestUtils.Keyboards.khmer_angkor.fullID))
let kbd = kmp1.findResource(withID: TestUtils.Keyboards.khmer_angkor.fullID)
XCTAssertNotNil(kbd)
XCTAssertEqual(kbd?.packageID, "khmer_angkor", "Keyboard package ID not properly set")
// This keyboard's not in the specified testing package.
XCTAssertNil(kmp1.findResource(withID: TestUtils.Keyboards.khmer10.fullID))
@ -87,7 +90,9 @@ class KeymanPackageTests: XCTestCase {
XCTFail("Incorrect package type loaded for test")
return
}
XCTAssertNotNil(kmp2.findResource(withID: TestUtils.LexicalModels.mtnt.fullID))
let lm = kmp2.findResource(withID: TestUtils.LexicalModels.mtnt.fullID)
XCTAssertNotNil(lm)
XCTAssertEqual(lm?.packageID, "nrc.en.mtnt", "Lexical model ID not properly set")
// Thanks to our package typing hierarchy, it's impossible to even TRY finding
// a FullKeyboardID within a LexicalModelKeymanPackage!