diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/CardTemplateJson.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/CardTemplateJson.kt new file mode 100644 index 000000000000..221214062113 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/CardTemplateJson.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Ashish Yadav + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.ichi2.anki.utils + +import com.ichi2.utils.deepClonedInto +import org.intellij.lang.annotations.Language +import org.json.JSONObject + +class CardTemplateJson : JSONObject { + /** + * Creates a new empty model object + */ + constructor() : super() + + /** + * Creates a copy from [JSONObject] and use it as a string + * + * This function will perform deepCopy on the passed object + * + * @see CardTemplateJson.from + */ + constructor(json: JSONObject) : super() { + json.deepClonedInto(this) + } + + /** + * Creates a model object form json string + */ + constructor(@Language("json") json: String) : super(json) + + val name: String + get() = getString("name") + + val ord: Int + get() = getInt("ord") + + val qfmt: String + get() = getString("qfmt") + + val afmt: String + get() = getString("afmt") + + val bqfmt: String + get() = getString("bqfmt") + + val bafmt: String + get() = getString("bafmt") + + val did: Long? + get() = if (isNull("did")) null else getLong("did") + + val bfont: String + get() = getString("bfont") + + val bsize: Int + get() = getInt("bsize") + + val id: Long + get() = getLong("id") +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/NoteType.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/NoteType.kt index 189c58df440e..46711cd69712 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/NoteType.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/NoteType.kt @@ -17,7 +17,9 @@ package com.ichi2.anki.utils.ext import anki.notetypes.StockNotetype.OriginalStockKind.ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION_VALUE +import com.ichi2.anki.utils.CardTemplateJson import com.ichi2.libanki.NotetypeJson +import com.ichi2.utils.jsonObjectIterable import org.json.JSONException /** @@ -29,3 +31,20 @@ val NotetypeJson.isImageOcclusion: Boolean } catch (e: JSONException) { false } + +/** + * Regular expression pattern for extracting cloze text fields. + */ +private val clozeRegex = "\\{\\{(?:.*?:)?cloze:([^}]*)\\}\\}".toRegex() + +val NotetypeJson.templates: List + get() = getJSONArray("tmpls").jsonObjectIterable().map { CardTemplateJson(it) } + +fun NotetypeJson.getAllClozeTextFields(): List { + if (!this.isCloze) { + throw IllegalStateException("getAllClozeTextFields called on non-cloze template") + } + + val questionFormat = templates.single().qfmt + return clozeRegex.findAll(questionFormat).map { it.groups[1]!!.value }.toList() +} diff --git a/AnkiDroid/src/test/java/com/ichi2/utils/NoteTypeTest.kt b/AnkiDroid/src/test/java/com/ichi2/utils/NoteTypeTest.kt new file mode 100644 index 000000000000..17e826fb3d24 --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/utils/NoteTypeTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 Ashish Yadav + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.ichi2.utils + +import com.ichi2.anki.utils.ext.getAllClozeTextFields +import com.ichi2.anki.utils.ext.templates +import com.ichi2.libanki.NotetypeJson +import junit.framework.TestCase.assertEquals +import kotlin.test.Test + +// link to a method in `NoteType.kt` for navigation as it contains no classes +/** Test of [NoteType][templates] */ +class NoteTypeTest { + + private val noteType = """ + { + "type":1, + "tmpls":[ + { + "name":"Cloze", + "ord":0, + "qfmt":"{{type:cloze:Text}} {{type:cloze:Text2}} {{cloze:Text3}} {{Added field}}", + "afmt":"{{cloze:Text}}
\n{{Back Extra}}", + "bqfmt":"", + "bafmt":"", + "did":null, + "bfont":"", + "bsize":0, + "id":1716321740 + } + ] + } + """ + + @Test + fun testQfmtField() { + val notetypeJson = NotetypeJson(noteType) + + val expectedQfmt = "{{type:cloze:Text}} {{type:cloze:Text2}} {{cloze:Text3}} {{Added field}}" + assertEquals(expectedQfmt, notetypeJson.templates[0].qfmt) + } + + @Test + fun testGetAllClozeTexts() { + val notetypeJson = NotetypeJson(noteType) + + val expectedClozeTexts = listOf("Text", "Text2", "Text3") + assertEquals(expectedClozeTexts, notetypeJson.getAllClozeTextFields()) + } + + @Test + fun testNameField() { + val notetypeJson = NotetypeJson(noteType) + val expectedName = "Cloze" + assertEquals(expectedName, notetypeJson.templates[0].name) + } + + @Test + fun testOrdField() { + val notetypeJson = NotetypeJson(noteType) + val expectedOrd = 0 + assertEquals(expectedOrd, notetypeJson.templates[0].ord) + } + + @Test + fun testAfmtField() { + val notetypeJson = NotetypeJson(noteType) + val expectedAfmt = "{{cloze:Text}}
\n{{Back Extra}}" + assertEquals(expectedAfmt, notetypeJson.templates[0].afmt) + } +}