Refactor the parser system to introduce a column system.

This commit is contained in:
Thomas Andres Gomez 2023-11-03 23:35:15 +01:00
parent ff1ac694bb
commit fd30b9c11e
25 changed files with 390 additions and 529 deletions

View file

@ -88,7 +88,7 @@ class AlterationRepository @Inject constructor(
suspend fun fetchAlterationSheet(sheets: List<CharacterSheet>) {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION)
val data = alterationParser.parse(values = request.execute(), sheets = sheets)
val data = alterationParser.parse(sheet = request.execute(), characterSheets = sheets)
_assignedAlterations.emit(data)
lastSuccessFullUpdate = Update.currentTime()
}

View file

@ -28,7 +28,7 @@ class DescriptionRepository @Inject constructor(
suspend fun fetchDescription() {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.DESCRIPTION)
val data = parser.parse(data = request.execute())
val data = parser.parse(sheet = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -28,7 +28,7 @@ class InventoryRepository @Inject constructor(
suspend fun fetchInventory() {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.INVENTORY)
val data = inventoryParser.parse(data = request.execute())
val data = inventoryParser.parse(sheet = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()
}

View file

@ -30,7 +30,7 @@ class SkillRepository @Inject constructor(
suspend fun fetchSkills() {
googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.SKILL)
val skills = skillParser.parse(data = request.execute())
val skills = skillParser.parse(sheet = request.execute())
_skills.emit(skills)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -43,8 +43,8 @@ class SpellRepository @Inject constructor(
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC_LEXICON).execute() },
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC).execute() },
)
val spellsBook = spellBookParser.parse(data = lexicon)
val assignedSpells = assignedSpellParser.parse(data = magic, spells = spellsBook)
val spellsBook = spellBookParser.parse(sheet = lexicon)
val assignedSpells = assignedSpellParser.parse(sheet = magic, spells = spellsBook)
this@SpellRepository.spellsBook = spellsBook
_spells.emit(assignedSpells)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -30,7 +30,7 @@ class LexiconRepository @Inject constructor(
suspend fun fetchLexicon() {
googleRepository.fetch { sheet ->
val request = sheet.get(LexiconBinder.ID, LexiconBinder.LEXICON)
val data = lexiconParser.parse(data = request.execute())
val data = lexiconParser.parse(sheet = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()

View file

@ -46,11 +46,11 @@ class LocationRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class)
private suspend fun updateData(map: ValueRange, marquee: ValueRange) {
val marquees = marqueeParser
.parse(data = marquee)
.parse(sheet = marquee)
.groupBy { it.map }
val maps = locationParser
.parse(data = map)
.parse(sheet = map)
.map {
val associatedMarquees = marquees[it.name]
if (associatedMarquees != null) {

View file

@ -35,7 +35,7 @@ class QuestRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class)
private suspend fun updateData(data: ValueRange) {
val questEntries = questParser.parse(value = data)
val questEntries = questParser.parse(sheet = data)
val questMap = questEntries.groupBy { it.title }
val quests = questMap.keys.mapIndexed { index, item ->
Quest(

View file

@ -5,8 +5,6 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class AttackParser @Inject constructor(
@ -16,34 +14,24 @@ class AttackParser @Inject constructor(
fun parse(
value: ValueRange,
charactersSheets: Map<String, CharacterSheet>,
): Map<String, List<Attack>> {
val sheet = value.values.sheet()
lateinit var structure: Map<String, Int>
): Map<String, List<Attack>> = parserScope {
// declare helper method to parse String
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
val actions = hashMapOf<String, MutableList<Attack>>()
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
structure = row.checkSheetStructure(model = COLUMNS)
}
row is List<*> -> {
value.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
// Assume that the name is the first column.
val characterSheet = charactersSheets[row.getOrNull(0) as? String ?: ""]
val title = row.parseString(NAME)
val characterSheet = charactersSheets[row.parse(CHARACTER)]
val title = row.parse(column = NAME)
if (characterSheet != null && title != null) {
val attack = Attack(
title = title,
type = parseType(value = row.parseString(TYPE)),
range = row.parseString(RANGE),
hit = throwParser.parse(value = row.parseString(HIT)),
damage = throwParser.parse(value = row.parseString(DAMAGE)),
type = parseType(value = row.parse(column = TYPE)),
range = row.parse(column = RANGE),
hit = throwParser.parse(value = row.parse(column = HIT)),
damage = throwParser.parse(value = row.parse(column = DAMAGE)),
)
actions
.getOrPut(characterSheet.name) { mutableListOf() }
@ -53,7 +41,7 @@ class AttackParser @Inject constructor(
}
}
return actions
actions
}
private fun parseType(value: String?): Attack.Type = try {
@ -64,12 +52,12 @@ class AttackParser @Inject constructor(
} ?: Attack.Type.PHYSICAL_MELEE_ATTACK
companion object {
const val NAME = "Nom"
const val TYPE = "Type"
const val RANGE = "Portée"
const val HIT = "Touché"
const val DAMAGE = "Dommage"
val COLUMNS get() = listOf("", NAME, TYPE, RANGE, HIT, DAMAGE)
private val CHARACTER = column("")
private val NAME = column("Nom")
private val TYPE = column("Type")
private val RANGE = column("Portée")
private val HIT = column("Touché")
private val DAMAGE = column("Dommage")
private val COLUMNS get() = listOf(CHARACTER, NAME, TYPE, RANGE, HIT, DAMAGE)
}
}

View file

@ -3,7 +3,6 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
@ -11,8 +10,7 @@ import javax.inject.Inject
class CharacterSheetParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(value: ValueRange): Map<String, CharacterSheet> {
fun parse(value: ValueRange): Map<String, CharacterSheet> = parserScope {
// fetch the character sheet
val sheet = value.values.sheet() // fetch the List<*>
?.mapNotNull { it as? List<*> } // transform the List<*> to a List<List<*>>
@ -20,79 +18,66 @@ class CharacterSheetParser @Inject constructor() {
val entryCount = if (sheet.isNotEmpty()) sheet[0].size else 0
List(entryCount) { index -> sheet.map { it.getOrNull(index) } }
}
// prepare a hash on the structure & function to get index in it.
lateinit var structure: Map<String, Int>
// declare helper method to parse String
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
// declare helper method to parse Int
fun List<*>.parseInt(key: String): Int? = parseString(key = key)?.toIntOrNull()
// parse the sheet
return sheet?.mapIndexedNotNull { index, item ->
sheet?.mapIndexedNotNull { index, item ->
when (index) {
0 -> {
structure = item.checkSheetStructure(model = ROWS)
return@mapIndexedNotNull null
updateStructure(row = item, columns = ROWS)
null
}
else -> {
val name = item.parseString(NAME)
val name = item.parse(column = NAME)
if (name != null) {
CharacterSheet(
name = name,
race = item.parseString(RACE),
proficiency = item.parseInt(MASTERY) ?: 2,
level = item.parseInt(LEVEL) ?: 2,
characterClass = item.parseString(CLASS) ?: "",
hitPoint = item.parseInt(MAX_HIT_POINT) ?: 1,
spell1 = item.parseInt(SPELL_LEVEL_1),
spell2 = item.parseInt(SPELL_LEVEL_2),
spell3 = item.parseInt(SPELL_LEVEL_3),
spell4 = item.parseInt(SPELL_LEVEL_4),
spell5 = item.parseInt(SPELL_LEVEL_5),
spell6 = item.parseInt(SPELL_LEVEL_6),
spell7 = item.parseInt(SPELL_LEVEL_7),
spell8 = item.parseInt(SPELL_LEVEL_8),
spell9 = item.parseInt(SPELL_LEVEL_9),
dC = item.parseInt(DD_SAVE_THROW),
armorClass = item.parseInt(ARMOR_CLASS) ?: 10,
speed = item.parseInt(SPEED) ?: 10,
strength = item.parseInt(STRENGTH) ?: 10,
dexterity = item.parseInt(DEXTERITY) ?: 10,
constitution = item.parseInt(CONSTITUTION) ?: 10,
intelligence = item.parseInt(INTELLIGENCE) ?: 10,
wisdom = item.parseInt(WISDOM) ?: 10,
charisma = item.parseInt(CHARISMA) ?: 10,
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW) ?: 0,
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW) ?: 0,
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW)
?: 0,
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW)
?: 0,
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW) ?: 0,
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW) ?: 0,
acrobatics = item.parseInt(ACROBATICS) ?: 0,
animalHandling = item.parseInt(ANIMAL_HANDLING) ?: 0,
arcana = item.parseInt(ARCANA) ?: 0,
athletics = item.parseInt(ATHLETICS) ?: 0,
deception = item.parseInt(DECEPTION) ?: 0,
history = item.parseInt(HISTORY) ?: 0,
insight = item.parseInt(INSIGHT) ?: 0,
intimidation = item.parseInt(INTIMIDATION) ?: 0,
investigation = item.parseInt(INVESTIGATION) ?: 0,
medicine = item.parseInt(MEDICINE) ?: 0,
nature = item.parseInt(NATURE) ?: 0,
perception = item.parseInt(PERCEPTION) ?: 0,
performance = item.parseInt(PERFORMANCE) ?: 0,
persuasion = item.parseInt(PERSUASION) ?: 0,
religion = item.parseInt(RELIGION) ?: 0,
sleightOfHand = item.parseInt(SLEIGHT_OF_HAND) ?: 0,
stealth = item.parseInt(STEALTH) ?: 0,
survival = item.parseInt(SURVIVAL) ?: 0,
race = item.parse(column = RACE),
proficiency = item.parseInt(column = MASTERY) ?: 2,
level = item.parseInt(column = LEVEL) ?: 2,
characterClass = item.parse(column = CLASS) ?: "",
hitPoint = item.parseInt(column = MAX_HIT_POINT) ?: 1,
spell1 = item.parseInt(column = SPELL_LEVEL_1),
spell2 = item.parseInt(column = SPELL_LEVEL_2),
spell3 = item.parseInt(column = SPELL_LEVEL_3),
spell4 = item.parseInt(column = SPELL_LEVEL_4),
spell5 = item.parseInt(column = SPELL_LEVEL_5),
spell6 = item.parseInt(column = SPELL_LEVEL_6),
spell7 = item.parseInt(column = SPELL_LEVEL_7),
spell8 = item.parseInt(column = SPELL_LEVEL_8),
spell9 = item.parseInt(column = SPELL_LEVEL_9),
dC = item.parseInt(column = DD_SAVE_THROW),
armorClass = item.parseInt(column = ARMOR_CLASS) ?: 10,
speed = item.parseInt(column = SPEED) ?: 10,
strength = item.parseInt(column = STRENGTH) ?: 10,
dexterity = item.parseInt(column = DEXTERITY) ?: 10,
constitution = item.parseInt(column = CONSTITUTION) ?: 10,
intelligence = item.parseInt(column = INTELLIGENCE) ?: 10,
wisdom = item.parseInt(column = WISDOM) ?: 10,
charisma = item.parseInt(column = CHARISMA) ?: 10,
strengthSavingThrows = item.parseInt(column = STRENGTH_SAVING_THROW) ?: 0,
dexteritySavingThrows = item.parseInt(column = DEXTERITY_SAVING_THROW) ?: 0,
constitutionSavingThrows = item.parseInt(column = CONSTITUTION_SAVING_THROW) ?: 0,
intelligenceSavingThrows = item.parseInt(column = INTELLIGENCE_SAVING_THROW) ?: 0,
wisdomSavingThrows = item.parseInt(column = WISDOM_SAVING_THROW) ?: 0,
charismaSavingThrows = item.parseInt(column = CHARISMA_SAVING_THROW) ?: 0,
acrobatics = item.parseInt(column = ACROBATICS) ?: 0,
animalHandling = item.parseInt(column = ANIMAL_HANDLING) ?: 0,
arcana = item.parseInt(column = ARCANA) ?: 0,
athletics = item.parseInt(column = ATHLETICS) ?: 0,
deception = item.parseInt(column = DECEPTION) ?: 0,
history = item.parseInt(column = HISTORY) ?: 0,
insight = item.parseInt(column = INSIGHT) ?: 0,
intimidation = item.parseInt(column = INTIMIDATION) ?: 0,
investigation = item.parseInt(column = INVESTIGATION) ?: 0,
medicine = item.parseInt(column = MEDICINE) ?: 0,
nature = item.parseInt(column = NATURE) ?: 0,
perception = item.parseInt(column = PERCEPTION) ?: 0,
performance = item.parseInt(column = PERFORMANCE) ?: 0,
persuasion = item.parseInt(column = PERSUASION) ?: 0,
religion = item.parseInt(column = RELIGION) ?: 0,
sleightOfHand = item.parseInt(column = SLEIGHT_OF_HAND) ?: 0,
stealth = item.parseInt(column = STEALTH) ?: 0,
survival = item.parseInt(column = SURVIVAL) ?: 0,
)
} else {
null
@ -103,54 +88,54 @@ class CharacterSheetParser @Inject constructor() {
}
companion object {
private const val NAME = "Nom"
private const val RACE = "Race"
private const val LEVEL = "Niveau"
private const val CLASS = "Classe"
private const val MAX_HIT_POINT = "Point de vie MAX"
private const val SPELL_LEVEL_1 = "Sort de niveau 1"
private const val SPELL_LEVEL_2 = "Sort de niveau 2"
private const val SPELL_LEVEL_3 = "Sort de niveau 3"
private const val SPELL_LEVEL_4 = "Sort de niveau 4"
private const val SPELL_LEVEL_5 = "Sort de niveau 5"
private const val SPELL_LEVEL_6 = "Sort de niveau 6"
private const val SPELL_LEVEL_7 = "Sort de niveau 7"
private const val SPELL_LEVEL_8 = "Sort de niveau 8"
private const val SPELL_LEVEL_9 = "Sort de niveau 9"
private const val DD_SAVE_THROW = "DD sauvergarde des sorts"
private const val ARMOR_CLASS = "Classe d'armure"
private const val SPEED = "Vitesse"
private const val MASTERY = "Bonus de maîtrise"
private const val STRENGTH = "Force"
private const val DEXTERITY = "Dextérité"
private const val CONSTITUTION = "Constitution"
private const val INTELLIGENCE = "Intelligence"
private const val WISDOM = "Sagesse"
private const val CHARISMA = "Charisme"
private const val STRENGTH_SAVING_THROW = "Jet de sauvegarde: Force"
private const val DEXTERITY_SAVING_THROW = "Jet de sauvegarde: Dextérité"
private const val CONSTITUTION_SAVING_THROW = "Jet de sauvegarde: Constitution"
private const val INTELLIGENCE_SAVING_THROW = "Jet de sauvegarde: Intelligence"
private const val WISDOM_SAVING_THROW = "Jet de sauvegarde: Sagesse"
private const val CHARISMA_SAVING_THROW = "Jet de sauvegarde: Charisme"
private const val ACROBATICS = "Acrobaties"
private const val ANIMAL_HANDLING = "Dressage"
private const val ARCANA = "Arcanes"
private const val ATHLETICS = "Athlétisme"
private const val DECEPTION = "Tromperie"
private const val HISTORY = "Histoire"
private const val INSIGHT = "Intuition"
private const val INTIMIDATION = "Intimidation"
private const val INVESTIGATION = "Investigation"
private const val MEDICINE = "Médecine"
private const val NATURE = "Nature"
private const val PERCEPTION = "Perception"
private const val PERFORMANCE = "Représentation"
private const val PERSUASION = "Persuasion"
private const val RELIGION = "Religion"
private const val SLEIGHT_OF_HAND = "Escamotage"
private const val STEALTH = "Discrétion"
private const val SURVIVAL = "Survie"
private val NAME = column("Nom")
private val RACE = column("Race")
private val LEVEL = column("Niveau")
private val CLASS = column("Classe")
private val MAX_HIT_POINT = column("Point de vie MAX")
private val SPELL_LEVEL_1 = column("Sort de niveau 1")
private val SPELL_LEVEL_2 = column("Sort de niveau 2")
private val SPELL_LEVEL_3 = column("Sort de niveau 3")
private val SPELL_LEVEL_4 = column("Sort de niveau 4")
private val SPELL_LEVEL_5 = column("Sort de niveau 5")
private val SPELL_LEVEL_6 = column("Sort de niveau 6")
private val SPELL_LEVEL_7 = column("Sort de niveau 7")
private val SPELL_LEVEL_8 = column("Sort de niveau 8")
private val SPELL_LEVEL_9 = column("Sort de niveau 9")
private val DD_SAVE_THROW = column("DD sauvergarde des sorts")
private val ARMOR_CLASS = column("Classe d'armure")
private val SPEED = column("Vitesse")
private val MASTERY = column("Bonus de maîtrise")
private val STRENGTH = column("Force")
private val DEXTERITY = column("Dextérité")
private val CONSTITUTION = column("Constitution")
private val INTELLIGENCE = column("Intelligence")
private val WISDOM = column("Sagesse")
private val CHARISMA = column("Charisme")
private val STRENGTH_SAVING_THROW = column("Jet de sauvegarde: Force")
private val DEXTERITY_SAVING_THROW = column("Jet de sauvegarde: Dextérité")
private val CONSTITUTION_SAVING_THROW = column("Jet de sauvegarde: Constitution")
private val INTELLIGENCE_SAVING_THROW = column("Jet de sauvegarde: Intelligence")
private val WISDOM_SAVING_THROW = column("Jet de sauvegarde: Sagesse")
private val CHARISMA_SAVING_THROW = column("Jet de sauvegarde: Charisme")
private val ACROBATICS = column("Acrobaties")
private val ANIMAL_HANDLING = column("Dressage")
private val ARCANA = column("Arcanes")
private val ATHLETICS = column("Athlétisme")
private val DECEPTION = column("Tromperie")
private val HISTORY = column("Histoire")
private val INSIGHT = column("Intuition")
private val INTIMIDATION = column("Intimidation")
private val INVESTIGATION = column("Investigation")
private val MEDICINE = column("Médecine")
private val NATURE = column("Nature")
private val PERCEPTION = column("Perception")
private val PERFORMANCE = column("Représentation")
private val PERSUASION = column("Persuasion")
private val RELIGION = column("Religion")
private val SLEIGHT_OF_HAND = column("Escamotage")
private val STEALTH = column("Discrétion")
private val SURVIVAL = column("Survie")
private val ROWS
get() = listOf(

View file

@ -0,0 +1,11 @@
package com.pixelized.rplexicon.repository.parser
data class Column(
val names: List<String>,
) {
override fun toString(): String {
return names.toString()
}
}
fun column(vararg names: String) = Column(names = names.toList())

View file

@ -3,32 +3,23 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Description
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class DescriptionParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Description> {
val sheet = data.values.sheet()
val values = hashMapOf<String, Description>()
lateinit var structure: Map<String, Int>
sheet?.forEachIndexed { index, item ->
when {
index == 0 -> {
structure = item.checkSheetStructure(model = COLUMNS)
}
item is List<*> -> {
val name = item.getOrNull(structure.getValue(NAME)) as? String
val translate = item.getOrNull(structure.getValue(TRANSLATE)) as? String
val description = item.getOrNull(structure.getValue(DESCRIPTION)) as? String
fun parse(sheet: ValueRange): Map<String, Description> = parserScope {
val descriptions = hashMapOf<String, Description>()
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val translate = row.parse(column = TRANSLATE)
val description = row.parse(column = DESCRIPTION)
if (name != null && translate != null && description != null) {
values[name] = Description(
descriptions[name] = Description(
name = name,
original = translate,
description = description,
@ -38,13 +29,13 @@ class DescriptionParser @Inject constructor() {
}
}
return values
return@parserScope descriptions
}
companion object {
private const val NAME = "Nom"
private const val TRANSLATE = "Traduction"
private const val DESCRIPTION = "Description"
private val NAME = column("Nom")
private val TRANSLATE = column("Traduction")
private val DESCRIPTION = column("Description")
private val COLUMNS = listOf(
NAME,

View file

@ -3,8 +3,6 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class LexiconParser @Inject constructor(
@ -12,80 +10,65 @@ class LexiconParser @Inject constructor(
private val genderParser: GenderParser,
private val raceParser: RaceParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Lexicon> {
val sheet = data.values.sheet()
lateinit var sheetStructure: Map<String, Int>
fun parse(sheet: ValueRange): List<Lexicon> = parserScope {
var id = 0
val lexicons = mutableListOf<Lexicon>()
return sheet?.mapIndexedNotNull { index, row ->
when {
index == 0 -> {
sheetStructure = row.checkSheetStructure(model = COLUMNS)
null
}
row is List<*> -> {
val name = row.getOrNull(sheetStructure.name) as? String
val diminutive = row.getOrNull(sheetStructure.diminutive) as? String
val gender = row.getOrNull(sheetStructure.gender) as? String
val race = row.getOrNull(sheetStructure.race) as? String
val portrait = row.getOrNull(sheetStructure.portrait) as? String
val status = row.getOrNull(sheetStructure.status) as? String
val location = row.getOrNull(sheetStructure.location) as? String
val description = row.getOrNull(sheetStructure.description) as? String
val history = row.getOrNull(sheetStructure.history) as? String
val tags = row.getOrNull(sheetStructure.tags) as? String
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
if (name != null) {
Lexicon(
val lexicon = Lexicon(
id = id++,
sheetIndex = index,
name = name,
diminutive = diminutive?.takeIf { it.isNotBlank() },
gender = genderParser.parse(gender),
race = raceParser.parser(race),
portrait = portraitParser.parse(portrait),
status = status?.takeIf { it.isNotBlank() },
location = location?.takeIf { it.isNotBlank() },
description = description?.takeIf { it.isNotBlank() },
history = history?.takeIf { it.isNotBlank() },
tags = tags?.takeIf { it.isNotBlank() },
diminutive = row.parse(column = SHORT),
gender = genderParser.parse(row.parse(column = GENDER)),
race = raceParser.parser(row.parse(column = RACE)),
status = row.parse(column = STATUS),
location = row.parse(column = LOCATION),
portrait = portraitParser.parse(row.parse(column = PORTRAIT)),
description = row.parse(column = DESCRIPTION),
history = row.parse(column = HISTORY),
tags = row.parse(column = TAGS),
)
} else {
null
lexicons.add(lexicon)
}
}
}
}
else -> null
return@parserScope lexicons
}
} ?: emptyList()
}
private val Map<String, Int>.name: Int get() = getValue(COLUMNS[0])
private val Map<String, Int>.diminutive: Int get() = getValue(COLUMNS[1])
private val Map<String, Int>.gender: Int get() = getValue(COLUMNS[2])
private val Map<String, Int>.race: Int get() = getValue(COLUMNS[3])
private val Map<String, Int>.status: Int get() = getValue(COLUMNS[4])
private val Map<String, Int>.location: Int get() = getValue(COLUMNS[5])
private val Map<String, Int>.portrait: Int get() = getValue(COLUMNS[6])
private val Map<String, Int>.description: Int get() = getValue(COLUMNS[7])
private val Map<String, Int>.history: Int get() = getValue(COLUMNS[8])
private val Map<String, Int>.tags: Int get() = getValue(COLUMNS[9])
companion object {
private val COLUMNS = listOf(
"Nom",
"Diminutif",
"Sexe",
"Race",
"Statut",
"Localisation",
"Portrait",
"Description",
"Histoire",
"Mots clés",
private val NAME = column("Nom")
private val SHORT = column("Diminutif")
private val GENDER = column("Sexe")
private val RACE = column("Race")
private val STATUS = column("Statut")
private val LOCATION = column("Localisation")
private val PORTRAIT = column("Portrait")
private val DESCRIPTION = column("Description")
private val HISTORY = column("Histoire")
private val TAGS = column("Mots clés")
private val COLUMNS
get() = listOf(
NAME,
SHORT,
GENDER,
RACE,
STATUS,
LOCATION,
PORTRAIT,
DESCRIPTION,
HISTORY,
TAGS,
)
}
}

View file

@ -1,55 +1,43 @@
package com.pixelized.rplexicon.repository.parser
import android.net.Uri
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class LocationParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Location> {
fun parse(sheet: ValueRange): List<Location> = parserScope {
val locations = mutableListOf<Location>()
var id = 0
var sheetStructure: Map<String, Int>? = null
return data.values.sheet()?.mapIndexedNotNull { index, item ->
when {
index == 0 -> {
sheetStructure = item.checkSheetStructure(COLUMNS)
null
}
item is List<*> -> {
val name = item.getOrNull(sheetStructure.name) as? String
val uri = item.getOrNull(sheetStructure.uri)?.toString()?.let { Uri.parse(it) }
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val uri = row.parseUri(column = MAP)
if (name != null && uri != null) {
Location(
val location = Location(
id = id++,
sheetIndex = index,
name = name,
uri = uri,
marquees = emptyList(),
)
} else {
null
locations.add(location)
}
}
}
}
else -> null
return@parserScope locations
}
} ?: emptyList()
}
private val Map<String, Int>?.name: Int get() = this?.getValue(COLUMNS[0]) ?: 0
private val Map<String, Int>?.uri: Int get() = this?.getValue(COLUMNS[1]) ?: 1
companion object {
private val COLUMNS = listOf(
"Nom", "Carte"
)
private val NAME = column("Nom")
private val MAP = column("Carte")
private val COLUMNS get() = listOf(NAME, MAP)
}
}

View file

@ -3,63 +3,52 @@ package com.pixelized.rplexicon.repository.parser
import androidx.compose.ui.geometry.Offset
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class MarqueeParser @Inject constructor() {
fun parse(data: ValueRange): List<Location.Marquee> {
val sheet = data.values.sheet()
lateinit var structure: Map<String, Int>
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<Location.Marquee> = parserScope {
val marquees = mutableListOf<Location.Marquee>()
return sheet?.mapIndexedNotNull { index, item ->
when {
index == 0 -> {
structure = item.checkSheetStructure(model = COLUMNS)
null
}
sheet.forEachRow { index, item ->
when (index) {
0 -> updateStructure(row = item, columns = COLUMNS)
item is List<*> -> {
val map = item.getOrNull(structure.map) as? String
val name = item.getOrNull(structure.name) as? String?
val x = (item.getOrNull(structure.x) as? String)
else -> {
val map = item.parse(column = MAP)
val x = item.parse(column = X)
?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull()
val y = (item.getOrNull(structure.y) as? String)
val y = item.parse(column = Y)
?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull()
val description = item.getOrNull(structure.description) as? String?
if (map != null) {
Location.Marquee(
val marquee = Location.Marquee(
map = map,
name = name?.takeIf { it.isNotBlank() },
name = item.parse(column = NAME),
position = when {
x != null && y != null -> Offset(x, y)
else -> Offset.Unspecified
},
description = description?.takeIf { it.isNotBlank() },
description = item.parse(column = DESCRIPTION),
)
} else {
null
marquees.add(marquee)
}
}
}
}
else -> null
return@parserScope marquees
}
} ?: emptyList()
}
private val Map<String, Int>.map: Int get() = getValue(COLUMNS[0])
private val Map<String, Int>.name: Int get() = getValue(COLUMNS[1])
private val Map<String, Int>.x: Int get() = getValue(COLUMNS[2])
private val Map<String, Int>.y: Int get() = getValue(COLUMNS[3])
private val Map<String, Int>.description: Int get() = getValue(COLUMNS[4])
companion object {
private val COLUMNS = listOf(
"Carte", "Nom", "X", "Y", "Description"
)
private val MAP = column("Carte")
private val NAME = column("Nom")
private val X = column("X")
private val Y = column("Y")
private val DESCRIPTION = column("Description")
private val COLUMNS get() = listOf(MAP, NAME, X, Y, DESCRIPTION)
}
}

View file

@ -36,8 +36,8 @@ class ObjectActionParser @Inject constructor(
}
companion object {
private const val NAME = "Nom"
private const val EFFECT = "Effet"
private val NAME = column("Nom")
private val EFFECT = column("Effet")
private val COLUMNS = listOf(NAME, EFFECT)
}
}

View file

@ -8,28 +8,28 @@ import javax.inject.Inject
class QuestParser @Inject constructor(
private val imageParser: PortraitParser
) {
fun parse(value: ValueRange): List<QuestEntry> = parserScope {
fun parse(sheet: ValueRange): List<QuestEntry> = parserScope {
val quest = mutableListOf<QuestEntry>()
value.forEachRow { index, item ->
sheet.forEachRow { index, item ->
when (index) {
0 -> updateStructure(row = item, columns = COLUMNS)
else -> {
val title = item.parse(TITLE)
val description = item.parse(DESCRIPTION)
val title = item.parse(column = TITLE)
val description = item.parse(column = DESCRIPTION)
if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
val entry = QuestEntry(
sheetIndex = index,
title = title,
subtitle = item.parse(SUB_TITLE),
complete = item.parseBool(COMPLETED) ?: false,
questGiver = item.parse(QUEST_GIVER),
area = item.parse(AREA),
groupReward = item.parse(GROUP_REWARD),
individualReward = item.parse(INDIVIDUAL_REWARD),
subtitle = item.parse(column = SUB_TITLE),
complete = item.parseBool(column = COMPLETED) ?: false,
questGiver = item.parse(column = QUEST_GIVER),
area = item.parse(column = AREA),
groupReward = item.parse(column = GROUP_REWARD),
individualReward = item.parse(column = INDIVIDUAL_REWARD),
description = description,
images = imageParser.parse(item.parse(IMAGE)),
background = item.parseUri(BACKGROUND),
images = imageParser.parse(item.parse(column = IMAGE)),
background = item.parseUri(column = BACKGROUND),
)
quest.add(entry)
}
@ -37,22 +37,23 @@ class QuestParser @Inject constructor(
}
}
quest
return@parserScope quest
}
companion object {
private const val TITLE = "Titre"
private const val SUB_TITLE = "Sous Titre"
private const val COMPLETED = "Compléter"
private const val QUEST_GIVER = "Commanditaire"
private const val AREA = "Lieu"
private const val GROUP_REWARD = "Récompense de groupe"
private const val INDIVIDUAL_REWARD = "Récompense individuelle"
private const val DESCRIPTION = "Description"
private const val IMAGE = "Image"
private const val BACKGROUND = "fond" // TODO
private val TITLE = column("Titre")
private val SUB_TITLE = column("Sous Titre")
private val COMPLETED = column("Compléter")
private val QUEST_GIVER = column("Commanditaire")
private val AREA = column("Lieu")
private val GROUP_REWARD = column("Récompense de groupe")
private val INDIVIDUAL_REWARD = column("Récompense individuelle")
private val DESCRIPTION = column("Description")
private val IMAGE = column("Image")
private val BACKGROUND = column("fond", "Fond") // TODO remove "fond" after 0.7.0 release
private val COLUMNS = listOf(
private val COLUMNS
get() = listOf(
TITLE,
SUB_TITLE,
COMPLETED,

View file

@ -13,9 +13,9 @@ inline fun <reified T> parserScope(
}
class SheetParserScope<T> {
private var structure: Map<String, Int> = hashMapOf()
private var structure: Map<Column, Int> = hashMapOf()
fun updateStructure(row: Any, columns: List<String>) {
fun updateStructure(row: Any, columns: List<Column>) {
structure = row.checkSheetStructure(model = columns)
}
@ -31,15 +31,15 @@ class SheetParserScope<T> {
fun Any?.toItem(): String? =
this.toString().takeIf { it.isNotBlank() }
fun List<*>.parse(column: String): String? =
fun List<*>.parse(column: Column): String? =
getOrNull(structure.getValue(column))?.toItem()
fun List<*>.parseInt(column: String): Int? =
fun List<*>.parseInt(column: Column): Int? =
parse(column)?.toIntOrNull()
fun List<*>.parseBool(column: String): Boolean? =
fun List<*>.parseBool(column: Column): Boolean? =
parse(column)?.equals("TRUE", ignoreCase = true)
fun List<*>.parseUri(column: String): Uri? =
fun List<*>.parseUri(column: Column): Uri? =
parse(column)?.takeIf { it.isNotBlank() }?.toUri()
}

View file

@ -3,30 +3,19 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class SkillParser @Inject constructor(
private val throwParser: ThrowParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, List<Skill>> {
val sheet = data.values.sheet()
val values = hashMapOf<String, MutableList<Skill>>()
fun parse(sheet: ValueRange): Map<String, List<Skill>> = parserScope {
val skills = hashMapOf<String, MutableList<Skill>>()
lateinit var sheetStructure: Map<String, Int>
fun List<*>.parse(column: String): String? =
(getOrNull(sheetStructure.getValue(column)) as? String)?.takeIf { it.isNotBlank() }
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
sheetStructure = row.checkSheetStructure(model = COLUMNS)
}
row is List<*> -> {
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val character = row[0] as? String
val name = row.parse(column = NAME)
if (character?.isNotBlank() == true && name != null) {
@ -37,28 +26,22 @@ class SkillParser @Inject constructor(
cost = row.parse(column = COST),
effect = throwParser.parse(row.parse(column = EFFECT)),
)
values.getOrPut(character) { mutableListOf() }.add(skill)
skills.getOrPut(character) { mutableListOf() }.add(skill)
}
}
}
}
return values
return@parserScope skills
}
companion object {
private const val NAME = "Nom"
private const val AMOUNT = "Quantité"
private const val REST = "Récupération"
private const val COST = "Coût"
private const val EFFECT = "Effect"
private val NAME = column("Nom")
private val AMOUNT = column("Quantité")
private val REST = column("Récupération")
private val COST = column("Coût")
private val EFFECT = column("Effect")
private val COLUMNS = listOf(
NAME,
AMOUNT,
REST,
COST,
EFFECT,
)
private val COLUMNS get() = listOf(NAME, AMOUNT, REST, COST, EFFECT)
}
}

View file

@ -4,11 +4,11 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.repository.parser.roll.DiceParser
import com.pixelized.rplexicon.repository.parser.roll.FlatValueParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class AlterationParser @Inject constructor(
@ -16,30 +16,22 @@ class AlterationParser @Inject constructor(
private val flatParser: FlatValueParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(values: ValueRange, sheets: List<CharacterSheet>): Map<String, List<Alteration>> {
val sheet = values.values.sheet()
fun parse(
sheet: ValueRange,
characterSheets: List<CharacterSheet>,
): Map<String, List<Alteration>> = parserScope {
val properties = Property.values()
val data = hashMapOf<String, MutableList<Alteration>>()
val alterations = hashMapOf<String, MutableList<Alteration>>()
lateinit var alterationStructure: Map<String, Int>
lateinit var charactersStructure: Map<String, Int>
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS + characterSheets.map { column(it.name) })
fun List<*>.parseString(key: String): String? =
(getOrNull(alterationStructure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
alterationStructure = row.checkSheetStructure(model = COLUMNS)
charactersStructure = row.checkSheetStructure(model = sheets.map { it.name })
}
row is List<*> -> {
else -> {
// Assume that the name is the first column.
val name = row.getOrNull(0) as? String
val source = row.parseString(SOURCE)
val target = row.parseString(TARGET)
val source = row.parse(column = SOURCE)
val target = row.parse(column = TARGET)
val alteration = if (name != null && source != null && target != null) {
Alteration(
name = name,
@ -48,8 +40,7 @@ class AlterationParser @Inject constructor(
active = false,
status = properties
.mapNotNull { property ->
val column = alterationStructure.getValue(property.key)
val value = row.getOrNull(column) as? String
val value = row.parse(column = column(property.key))
if (value?.isNotEmpty() == true) {
property to parseAlterationStatus(name, value)
} else {
@ -62,26 +53,26 @@ class AlterationParser @Inject constructor(
null
}
if (alteration != null) {
sheets
characterSheets
.filter { // check if the alteration is applicable to the character
alteration.target.let { target ->
target == ALL || target == it.characterClass || target == it.race || target == it.name
}
}
.forEach { // check the default alteration state for that character
val column = charactersStructure.getValue(it.name)
val default = (row.getOrNull(column) as? String)?.toBoolean()
val default = row.parseBool(column = column(it.name))
val assignedAlteration = alteration.copy(
active = default ?: false
)
data.getOrPut(it.name) { mutableListOf() }.add(assignedAlteration)
alterations.getOrPut(it.name) { mutableListOf() }
.add(assignedAlteration)
}
}
}
}
}
return data
return@parserScope alterations
}
private fun parseAlterationStatus(name: String, value: String): Alteration.Status =
@ -125,9 +116,9 @@ class AlterationParser @Inject constructor(
private const val DISADVANTAGE = "dis"
private const val FAIL = "fail"
private const val TARGET = "Cible"
private const val SOURCE = "Source"
private val TARGET = column("Cible")
private val SOURCE = column("Source")
private val COLUMNS
get() = listOf(SOURCE, TARGET) + Property.values().map { it.key }
get() = listOf(SOURCE, TARGET) + Property.values().map { column(it.key) }
}
}

View file

@ -1,33 +1,28 @@
package com.pixelized.rplexicon.repository.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class AssignedAlterationParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(value: ValueRange, characters: List<String>): Map<String, List<String>> {
val sheet = value.values.sheet()
fun parse(
sheet: ValueRange,
characterNames: List<String>
): Map<String, List<String>> = parserScope {
val status = hashMapOf<String, MutableList<String>>()
lateinit var structure: Map<String, Int>
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed { index, row ->
sheet.forEachRow { index, row ->
when (index) {
0 -> {
structure = row.checkSheetStructure(model = characters)
}
0 -> updateStructure(row = row, columns = characterNames.map { column(it) })
else -> {
val alteration = row.getOrNull(0)?.toString()
if (alteration != null) {
characters.forEach { character ->
val value = row.getOrNull(structure.getValue(character))
if (value == TRUE) {
characterNames.forEach { character ->
if (row.parseBool(column = column(character)) == true) {
status
.getOrPut(character) { mutableListOf() }
.add(alteration)
@ -38,10 +33,6 @@ class AssignedAlterationParser @Inject constructor() {
}
}
return status
}
companion object {
private const val TRUE = "TRUE"
return@parserScope status
}
}

View file

@ -2,6 +2,7 @@ package com.pixelized.rplexicon.repository.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -9,10 +10,10 @@ import javax.inject.Inject
class InventoryParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Inventory> = parserScope {
fun parse(sheet: ValueRange): Map<String, Inventory> = parserScope {
val inventories = hashMapOf<String, Inventory.Builder>()
data.forEachRow { index, row ->
sheet.forEachRow { index, row ->
when {
index == 0 -> updateStructure(row = row, columns = COLUMNS)
@ -46,9 +47,9 @@ class InventoryParser @Inject constructor() {
}
companion object {
private const val CONTAINER = "Contenant"
private const val NAME = "Name"
private const val AMOUNT = "Quantité"
private val CONTAINER = column("Contenant")
private val NAME = column("Name")
private val AMOUNT = column("Quantité")
private val COLUMNS = listOf(CONTAINER, NAME, AMOUNT)
}
}

View file

@ -1,35 +1,28 @@
package com.pixelized.rplexicon.repository.parser.spell
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.ThrowParser
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.ThrowParser
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class AssignedSpellParser @Inject constructor(
private val throwParser: ThrowParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange, spells: List<Spell>): Map<String, List<AssignedSpell>> {
val sheet = data.values.sheet()
fun parse(
sheet: ValueRange,
spells: List<Spell>,
): Map<String, List<AssignedSpell>> = parserScope {
val spellsBook = spells.associateBy { it.name }
lateinit var structure: Map<String, Int>
val assignedSpells = hashMapOf<String, MutableList<AssignedSpell>>()
// declare helper method to parse String
fun List<*>.parse(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
structure = row.checkSheetStructure(model = COLUMNS)
}
row is List<*> -> {
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val character = row.getOrNull(0) as? String
val spell = spellsBook[row.parse(NAME)]
if (character != null && spell != null) {
@ -46,22 +39,15 @@ class AssignedSpellParser @Inject constructor(
}
}
}
return assignedSpells
return@parserScope assignedSpells
}
companion object {
private const val NAME = "Nom"
private const val HIT = "Touché"
private const val EFFECT = "Effet"
private const val LEVEL = "Par niveau"
private val NAME = column("Nom")
private val HIT = column("Touché")
private val EFFECT = column("Effet")
private val LEVEL = column("Par niveau")
private val COLUMNS
get() = listOf(
NAME,
HIT,
EFFECT,
LEVEL,
)
private val COLUMNS get() = listOf(NAME, HIT, EFFECT, LEVEL)
}
}

View file

@ -3,40 +3,27 @@ package com.pixelized.rplexicon.repository.parser.spell
import android.util.Log
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject
class SpellBookParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Spell> {
val sheet = data.values.sheet()
lateinit var structure: Map<String, Int>
// declare helper method to parse String
fun List<*>.parse(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
return sheet?.mapIndexedNotNull { index, row ->
fun parse(sheet: ValueRange): List<Spell> = parserScope {
val spells = mutableListOf<Spell>()
sheet.forEachRow { index, row ->
when {
index == 0 -> {
structure = row.checkSheetStructure(model = COLUMNS)
null
}
row is List<*> -> {
val name = row.parse(NAME)
val level = row.parse(LEVEL)?.toIntOrNull()
val school = parseSchool(row.parse(SCHOOL))
val castingTime = row.parse(CASTING_TIME)
val range = row.parse(RANGE)
val requirement = row.parse(REQUIREMENT)
val duration = row.parse(DURATION)
val ritual = row.parse(RITUAL)?.toBoolean() ?: false
index == 0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val level = row.parseInt(column = LEVEL)
val school = parseSchool(row.parse(column = SCHOOL))
val castingTime = row.parse(column = CASTING_TIME)
val range = row.parse(column = RANGE)
val requirement = row.parse(column = REQUIREMENT)
val duration = row.parse(column = DURATION)
if (name != null
&& level != null
&& school != null
@ -45,7 +32,7 @@ class SpellBookParser @Inject constructor() {
&& requirement != null
&& duration != null
) {
Spell(
val spell = Spell(
name = name,
level = level,
school = school,
@ -53,16 +40,14 @@ class SpellBookParser @Inject constructor() {
range = range,
requirement = requirement,
duration = duration,
ritual = ritual,
ritual = row.parseBool(column = RITUAL) ?: false,
)
} else {
null
spells.add(spell)
}
}
else -> null
}
} ?: emptyList()
}
return@parserScope spells
}
private fun parseSchool(value: String?): Spell.School? = try {
@ -73,25 +58,16 @@ class SpellBookParser @Inject constructor() {
}
companion object {
private const val NAME = "Nom"
private const val SCHOOL = "École"
private const val LEVEL = "Niveau"
private const val CASTING_TIME = "Temps d'incantation"
private const val RANGE = "Portée"
private const val REQUIREMENT = "Composantes"
private const val DURATION = "Durée"
private const val RITUAL = "Rituel"
private val NAME = column("Nom")
private val SCHOOL = column("École")
private val LEVEL = column("Niveau")
private val CASTING_TIME = column("Temps d'incantation")
private val RANGE = column("Portée")
private val REQUIREMENT = column("Composantes")
private val DURATION = column("Durée")
private val RITUAL = column("Rituel")
private val COLUMNS
get() = listOf(
NAME,
LEVEL,
SCHOOL,
CASTING_TIME,
RANGE,
REQUIREMENT,
DURATION,
RITUAL,
)
get() = listOf(NAME, LEVEL, SCHOOL, CASTING_TIME, RANGE, REQUIREMENT, DURATION, RITUAL)
}
}

View file

@ -1,30 +1,27 @@
package com.pixelized.rplexicon.utilitary.extentions.local
import com.pixelized.rplexicon.repository.parser.Column
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@Throws(IncompatibleSheetStructure::class)
fun Any?.checkSheetStructure(model: List<String>): HashMap<String, Int> {
fun Any?.checkSheetStructure(model: List<Column>): Map<Column, Int> {
// check if the row is a list
if (this !is ArrayList<*>) {
throw IncompatibleSheetStructure("First row is not a List: $this")
}
// parse the first line to find element that we recognize.
val sheetStructure = hashMapOf<String, Int>()
forEachIndexed { index, cell ->
if (cell is String && model.contains(cell)) {
sheetStructure[cell] = index
val sheetStructure = model.associateWith { key ->
indexOfFirst { cell ->
key.names.contains(cell)
}
}
// check if we found everything we need.
when {
sheetStructure.size < model.size -> throw IncompatibleSheetStructure(
message = "Sheet header row does not have enough column: $size.\nstructure: $this\nheader: $sheetStructure"
)
sheetStructure.size > model.size -> throw IncompatibleSheetStructure(
message = "Sheet header row does have too mush columns: $size.\nstructure: $this\nheader: $sheetStructure"
sheetStructure.any { it.value < 0 } -> throw IncompatibleSheetStructure(
message = "Sheet header row does not have enough column !" + sheetStructure
.filter { it.value < 0 }.keys
.joinToString(prefix = "\n- Missing column: ") { it.toString() }
)
}
return sheetStructure
}