package database.table import database.data.Messagebank import database.dbFunctions import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.tinylog.Logger import java.sql.Connection import java.util.function.Consumer class Table_Messagebank(connection: Connection) : dbFunctions("messagebank", connection, listOf("index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS")) { override fun Create() { val tabledefinition = "CREATE TABLE IF NOT EXISTS ${super.dbName} (" + "`index` INT AUTO_INCREMENT PRIMARY KEY," + "Description VARCHAR(512) NOT NULL," + // Description of the message "Language VARCHAR(45) NOT NULL," + // Language of the message "ANN_ID INT NOT NULL," + // ANN ID of the message "Voice_Type VARCHAR(45) NOT NULL," + // Voice type of the message "Message_Detail VARCHAR(1024) NOT NULL," + // Full message text "Message_TAGS VARCHAR(1024)" + // Comma-separated tags for the message ")" super.Create(tabledefinition) } override fun Get(cbOK: Consumer?, cbFail: Consumer?) { List.clear() try { val statement = connection.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") while (resultSet?.next() == true) { val messagebank = Messagebank( resultSet.getLong("index").toUInt(), resultSet.getString("Description"), resultSet.getString("Language"), resultSet.getInt("ANN_ID").toUInt(), resultSet.getString("Voice_Type"), resultSet.getString("Message_Detail"), resultSet.getString("Message_TAGS") ) List.add(messagebank) } cbOK?.accept(Unit) } catch (e: Exception) { Logger.error("Error fetching ${super.dbName} : ${e.message}" as Any) cbFail?.accept("Error fetching ${super.dbName} : ${e.message}") } } override fun Add(data: Messagebank): Boolean { try { val statement = connection.prepareStatement("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)") statement?.setString(1, data.Description) statement?.setString(2, data.Language) statement?.setInt(3, data.ANN_ID.toInt()) statement?.setString(4, data.Voice_Type) statement?.setString(5, data.Message_Detail) statement?.setString(6, data.Message_TAGS) val rowsAffected = statement?.executeUpdate() if (rowsAffected != null && rowsAffected > 0) { Logger.info("Messagebank added: ${data.Description}" as Any) return true } else { Logger.warn("No messagebank entry added for: ${data.Description}" as Any) } } catch (e: Exception) { Logger.error("Error adding messagebank entry: ${e.message}" as Any) } return false } override fun AddAll(data: ArrayList): Boolean { try { connection.autoCommit = false val sql = "INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) VALUES (?, ?, ?, ?, ?, ?)" val statement = connection.prepareStatement(sql) for (mb in data) { statement.setString(1, mb.Description) statement.setString(2, mb.Language) statement.setInt(3, mb.ANN_ID.toInt()) statement.setString(4, mb.Voice_Type) statement.setString(5, mb.Message_Detail) statement.setString(6, mb.Message_TAGS) statement.addBatch() } statement.executeBatch() connection.commit() Logger.info("Bulk messagebank insert successful: ${data.size} entries" as Any) connection.autoCommit = true return true } catch (e: Exception) { Logger.error("Error adding messagebank entries: ${e.message}" as Any) } return false } override fun UpdateByIndex(index: Int, data: Messagebank): Boolean { try { val statement = connection.prepareStatement("UPDATE ${super.dbName} SET Description = ?, Language = ?, ANN_ID = ?, Voice_Type = ?, Message_Detail = ?, Message_TAGS = ? WHERE `index` = ?") statement?.setString(1, data.Description) statement?.setString(2, data.Language) statement?.setInt(3, data.ANN_ID.toInt()) statement?.setString(4, data.Voice_Type) statement?.setString(5, data.Message_Detail) statement?.setString(6, data.Message_TAGS) statement?.setLong(7, index.toLong()) val rowsAffected = statement?.executeUpdate() if (rowsAffected != null && rowsAffected > 0) { Logger.info("Messagebank updated at index $index: ${data.Description}" as Any) return true } else { Logger.warn("No messagebank entry updated at index $index for: ${data.Description}" as Any) } } catch (e: Exception) { Logger.error("Error updating messagebank entry at index $index: ${e.message}" as Any) } return false } override fun Resort(): Boolean { try { val statement = connection.createStatement() val tempdb_name = "temp_${super.dbName}" // use a temporary table to reorder the index statement?.executeUpdate("CREATE TABLE IF NOT EXISTS $tempdb_name LIKE ${super.dbName}") statement?.executeUpdate("TRUNCATE TABLE $tempdb_name") statement?.executeUpdate("INSERT INTO $tempdb_name (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM ${super.dbName} ORDER BY ANN_ID ") statement?.executeUpdate("TRUNCATE TABLE ${super.dbName}") statement?.executeUpdate("INSERT INTO ${super.dbName} (Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS) SELECT Description, Language, ANN_ID, Voice_Type, Message_Detail, Message_TAGS FROM $tempdb_name") statement?.executeUpdate("DROP TABLE $tempdb_name") Logger.info("${super.dbName} table resorted by Description" as Any) // reload the local list Get() return true } catch (e: Exception) { Logger.error("Error resorting ${super.dbName} table by Description: ${e.message}" as Any) } return false } override fun Import_XLSX(workbook: XSSFWorkbook): Boolean { try { // check if there is sheet named "Messagebank" val sheet = workbook.getSheet("Messagebank") ?: throw Exception("No sheet named 'Messagebank' found") // check if the sheet contains header named "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS" val headerRow = sheet.getRow(0) ?: throw Exception("No header row found") val headers = arrayOf( "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS" ) for ((colIndex, header) in headers.withIndex()) { val cell = headerRow.getCell(colIndex) ?: throw Exception("Header '$header' not found") if (cell.stringCellValue != header) throw Exception("Header '$header' not found") } // clear existing messagebank Clear() // read each row and insert into database val _messagebankList = ArrayList() for (rowIndex in 1..sheet.lastRowNum) { val row = sheet.getRow(rowIndex) ?: continue val description = row.getCell(1)?.stringCellValue ?: continue val language = row.getCell(2)?.stringCellValue ?: continue val annId = row.getCell(3)?.stringCellValue?.toUIntOrNull() ?: continue val voiceType = row.getCell(4)?.stringCellValue ?: continue val messageDetail = row.getCell(5)?.stringCellValue ?: continue val messageTags = row.getCell(6)?.stringCellValue ?: continue val messagebank = Messagebank(0u, description, language, annId, voiceType, messageDetail, messageTags) _messagebankList.add(messagebank) } return AddAll(_messagebankList) } catch (e: Exception) { Logger.error { "Error importing Messagebank, Msg: ${e.message}" } } return false } override fun Export_XLSX(): XSSFWorkbook? { try { val statement = connection.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM ${super.dbName}") val workbook = XSSFWorkbook() val sheet = workbook.createSheet("Messagebank") val headerRow = sheet.createRow(0) val headers = arrayOf( "Index", "Description", "Language", "ANN_ID", "Voice_Type", "Message_Detail", "Message_TAGS" ) for ((colIndex, header) in headers.withIndex()) { val cell = headerRow.createCell(colIndex) cell.setCellValue(header) } var rowIndex = 1 while (resultSet?.next() == true) { val row = sheet.createRow(rowIndex++) row.createCell(0).setCellValue(resultSet.getString("index")) row.createCell(1).setCellValue(resultSet.getString("Description")) row.createCell(2).setCellValue(resultSet.getString("Language")) row.createCell(3).setCellValue(resultSet.getString("ANN_ID")) row.createCell(4).setCellValue(resultSet.getString("Voice_Type")) row.createCell(5).setCellValue(resultSet.getString("Message_Detail")) row.createCell(6).setCellValue(resultSet.getString("Message_TAGS")) } for (i in headers.indices) { sheet.autoSizeColumn(i) } return workbook } catch (e: Exception) { Logger.error { "Error exporting Messagebank, Msg: ${e.message}" } } return null } /** * Get all distinct message ID from messagebank * @return a list of distinct ANN_ID sorted numerically */ fun Get_MessageID_List(): List { return List .distinctBy { it.ANN_ID } .map { it.ANN_ID } .sorted() } // valid messagedetail is message_name [ann_id] // so we need regex to check if messagedetail matches that format private val messageDetailRegex = """^(.*?)\s*\[(\d+)]$""".toRegex() /** * Check if a messagebank entry exists based on messagedetail and languages * @param messagedetail the messagedetail in format "message_name [ann_id]" * @param languages a comma or semicolon separated string of languages to check * @return true if the messagebank entry exists for all specified languages, false otherwise */ fun Messagebank_Exists(messagedetail: String, languages: String) : Boolean{ val match = messageDetailRegex.find(messagedetail) val ll = languages.split(",",";").map { it.trim() } if (match != null){ val msg = match.groupValues[1].trim() val annid = match.groupValues[2].toUIntOrNull() ?: return false // kalau bukan number, return false val ff = List.filter{ it.ANN_ID == annid && it.Description == msg } return ll.all{ lang -> ff.any{ it.Language.equals(lang, ignoreCase = true) } } } return false } }