mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-13 19:17:44 +00:00
Merge pull request #3234 from keymanapp/feat/ios/engine/package-ids
feat(ios/engine): KeymanPackage now parses its ID
This commit is contained in:
commit
fdac12885d
10 changed files with 79 additions and 22 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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] = []
|
||||
|
||||
|
|
|
|||
|
|
@ -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 []
|
||||
|
|
|
|||
|
|
@ -52,3 +52,8 @@ extension LanguageResource {
|
|||
return typedFullID
|
||||
}
|
||||
}
|
||||
|
||||
internal protocol KMPInitializableLanguageResource: LanguageResource {
|
||||
associatedtype Metadata: KMPResource
|
||||
init?(from metadata: Metadata, packageID: String, lgCode: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue