I am an amateur Kotlin programming. And I have one task which I am not sure how to solve.
So any help would come in handy.
More about this task:
I get this message via HTTP post request
AL:OK:XX:XX:XX:XX:XX:YY~~TYPE:3~~FOF:v1.0~~RSSI:-68~~PORT:8215~~TEMP:34.22~~CH1:OK~~CH2:KS~~CH3:PR~~CH4:VL~~CH5:KS~~CH6:OK~~AUX1:OK~~AUX2:KS~~AUX3:OK
And I need to parse this message into following output format.
XX:XX:XX:XX:XX:XY - MAC ADDRESS
3 - Type of device
v1.0 - Version of device
-68 - Signal strenght
8215 - Server Port
34.22 - Processor temperature
I only need to get this output in console
I would be really thankfull for any kind of help or hints about this problem.
Thank you in advance,
You can use regex group for this purpose and get each part with index. also you can Assign name to each group and then select each part you want with name.
import java.util.regex.Pattern
fun main() {
val request = "AL:OK:XX:XX:XX:XX:XX:YY~~TYPE:3~~FOF:v1.0~~RSSI:-68~~PORT:8215~~TEMP:34.22" +
"~~CH1:OK~~CH2:KS~~CH3:PR~~CH4:VL~~CH5:KS~~CH6:OK" +
"~~AUX1:OK~~AUX2:KS~~AUX3:OK"
extractValues(request)
}
fun extractValues(request: String) {
val patternWithName = "AL:OK:(?<macAddress>.+)~~TYPE:(?<type>\\d+)~~FOF:(?<fof>v\\d+.\\d+)~~RSSI:(?<rssi>[-+]\\d+)~~PORT:(?<port>\\d+)~~TEMP:(?<temp>\\d+.\\d+)" +
"~~CH1:(?<ch1>.+)~~CH2:(?<ch2>.+)~~CH3:(?<ch3>.+)~~CH4:(?<ch4>.+)~~CH5:(?<ch5>.+)~~CH6:(?<ch6>.+)" +
"~~AUX1:(?<aux1>.+)~~AUX2:(?<aux2>.+)~~AUX3:(?<aux3>.+)"
val patternWithoutName = "AL:OK:(.+)~~TYPE:(\\d+)~~FOF:(v\\d+.\\d+)~~RSSI:([-+]\\d+)~~PORT:(\\d+)~~TEMP:(\\d+.\\d+)" +
"~~CH1:(.+)~~CH2:(.+)~~CH3:(.+)~~CH4:(.+)~~CH5:(.+)~~CH6:(.+)" +
"~~AUX1:(.+)~~AUX2:(.+)~~AUX3:(.+)"
val matcher = Pattern.compile(patternWithoutName).matcher(request)
if (matcher.find()) {
// get with their name --> use [patternWithName]
// val macAddress = matcher.group("macAddress")
// val type = matcher.group("type")
// val fof = matcher.group("fof")
// or
// get with index -- use [patternWithName] or [patternWithoutName]
for (i in 1..matcher.groupCount()) {
println(matcher.group(i))
}
} else {
error("$request doesn't match.")
}
}
Related
I have a data class called Alert that contains the following
data class Alert(
val status: String,
val records: Set<String>
)
val orderedStatuses = getOrderedStatuses(date, statuses) returns a List<Alert> that currently holds 3 values each with its own set.
val orderedStatuses = getOrderedStatuses(date, statuses)
val alert = if (orderedStatuses.isEmpty()) {
"No alerts found"
} else {
"${orderedStatuses.size} alerts found "+
"Alerts:\n" +
orderedStatuses.joinToString("\n") {
"\t${it.status} : \n" +
"\t(${it.records})"
}
}
The output looks like this:
Alerts:
error_foo :
([000-00-0000-00-000000000000, 111-1111-111-111-111111111111])
error_bar :
([222-222-222-222-222222222222, 333-33-3333-33-333333333333])
error_blah :
([444-4444-44444-444-444444444444, 555-555-555-555-555555555555])
What I'm struggling to do is list the alerts in a format like the following
I want to fix the set to look like the following:
Alerts:
ERROR_foo :
('000-00-0000-00-000000000000', '111-1111-111-111-111111111111')
Error_bar :
('222-222-222-222-222222222222', '333-33-3333-33-333333333333')
Error_blah :
('444-4444-44444-444-444444444444', '555-555-555-555-555555555555')
I've been looking into functional programming with Kotlin but haven't found something that clicks in my head/works
thanks!
You need to modify the way you print the alert, I think the one need to modify is this part
"\t(${it.records})"
should call another joinToString() and a little transformation, here is how you can call it
"\t(${
it.records.joinToString(",") { record -> "'${record}'" }
})"
Here is the full code on the else block
"${orderedStatuses.size} alerts found " +
"Alerts:\n" +
orderedStatuses.joinToString("\n") {
"\t${it.status} : \n" +
"\t(${
it.records.joinToString(",") { record -> "'${record}'" }
})"
}
I don't know how to get the string in readable format in my app. My code is:
val allergyList = recipeItem.allergies
allergyList.joinToString()
var allergyString: String = ""
for (allergy in allergyList) {
allergyList[1]
allergyString += " ${allergy}"
println(allergy.toString())
}
holder.recipeSearchPageAllergies.text = allergyString
When I print this I get the allergy string memory space?
Result for each one is something like this:
Allergy#4e8f238
How do I 'decode' it into something readable for a human? It should say 'nut allergy'.
you have some options. If you have the control over the Allergy source code,
you could override toString method:
class Allergy(val name: String) {
override fun toString(): String = "Allergy[name=${name}]"
}
println(Allergy("flowers"))
// Allergy[name=flowers]
also, you can make a data class of it. Data class has sane toString by default. It also has a few nice perks, like by default equals/hashCode generation, and deconstruction to components, allowing you to use it in destructing:
data class Allergy(val name: String)
println(Allergy("peanuts"))
// Allergy(name=peanuts)
otherwise, if you can't modify the source of the Allregy, you can make up an extension method for that:
class Allergy(val name: String)
fun Allergy.readable() = "Allergy[name=${name}]"
println(Allergy("cats").readable())
// Allergy[name=cats]
in your case, you could also make an extension method for collections of allergies to have the format you need:
fun Collection<Allergy>.readable() = joinToString { "Allergy[name=${it.name}]" }
println(
listOf(Allergy("cats"), Allergy("peanuts"), Allergy("flowers")).readable()
)
// Allergy[name=cats], Allergy[name=peanuts], Allergy[name=flowers]
// in your case:
holder.recipeSearchPageAllergies.text = recipeItem.allergies.readable()
// or just
holder.recipeSearchPageAllergies.text = recipeItem.allergies.joinToString { "Allergy[name=${it.name}]" }
You can make it simplier:
val allergiesStr = recipeItem.allergies.map { allergy ->
// your allergy name from allergy variable
}.joinToString(separator = " ")
I tryed to fix a problem with encodings. So, I sent from 'Postman', from web browser request to server, where I search data in database by keys in request. Request can be like this:
http://localhost:8080/books.getBooksByGenre/Документальное/0/10
(in browser).
Server receive string, like
http://localhost:8080/books.getBooksByGenre/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5/0/10
then, takes params from url:
genreName: 'Документальное'
start: 0
count: 10.
Then, this data sends to dao:
override fun findGenreByName(genreName: String): DatabaseGenre {
return transaction(db) { getGenreByName(genreName) }
}
private fun getGenreByName(genreName: String): DatabaseGenre {
return try {
val foundGenre = GenreEntity.find { Genres.genre eq genreName }.single()
DatabaseGenre(foundGenre.id.value, foundGenre.genre, foundGenre.link)
} catch (e: Exception) {
throw NothingFoundInDatabaseException("no one genre found by '$genreName'")
} catch (e: NoSuchElementException) {
val m = "Duplicates of genre with name '$genreName'"
throw DuplicatedDataInDatabaseException(m)
}
}
In log I see, that sql-query for finding genres is correct, but I receive an exception:
java.util.NoSuchElementException: Collection is empty.
The sql-query, as I said, is correct:
SELECT genres.id, genres.genre, genres.link FROM genres WHERE genres.genre = 'Документальное'
Structure of genres table:
genres
id: int(10)
genre: varchar(100)
link: varchar(100
I tryied, to select all genres, and this query executed almost correctly. So, I decided, to check this query with english word, and this query correctly executed:
SELECT genres.id, genres.genre, genres.link FROM genres WHERE genres.genre = 'simpleGenre'
I have not exceptions with this query.
So, what I've done wrong and how to fix problem with collations?
UPD:
As I said at github (issue), I've tryied this query it mysql cli and I receive correct answer.
Also, I've tryed to decode url params (with java URLDecoder class). It doesn't helps too.
Thanks, #madhead.
I tryied an advance of #madhead, and it works. So, from this time my DB connection URL looks like this:
val connect = Database.connect(
url = "jdbc:mysql://localhost:3306/my_database_name?characterEncoding=utf8&useUnicode=true",
driver = "com.mysql.jdbc.Driver",
user = user_name,
password = password
)
Background:
I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).
If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.
One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.
Question:
Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:
def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
parameters.foreach(p =>
// Tell the handler to handle this specific parameter
case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
someResultFunction(p)
)
}
Is this possible in Acolyte? If so, please provide an example.
It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.
See the code below for an example:
import java.sql.DriverManager
import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec
class AcolyteTest extends FunSpec {
describe("Using pattern matching to extract a query parameter") {
it("should extract the parameter and make it usable for dynamic result returning") {
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val handlerName = "testOneHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
rows.append(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "hello world")
val resultSet = preparedStatement.executeQuery()
resultSet.next()
assertResult(resultSet.getString(1))("hello world")
}
it("should support a slightly more complex example") {
val firstResult = "The first result"
val secondResult = "The second result"
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val results: Map[String, RowList1.Impl[String]] = Map(
"one" -> rows.append(firstResult),
"two" -> rows.append(secondResult)
)
def getResult(parameter: String): QueryResult = {
results.get(parameter) match {
case Some(row) => row.asResult()
case _ => acolyte.jdbc.QueryResult.Nil
}
}
val handlerName = "testTwoHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
getResult(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "one")
val resultSetOne = preparedStatement.executeQuery()
resultSetOne.next()
assertResult(resultSetOne.getString(1))(firstResult)
preparedStatement.setString(1, "two")
val resultSetTwo = preparedStatement.executeQuery()
resultSetTwo.next()
assertResult(resultSetTwo.getString(1))(secondResult)
}
}
}
I have been looking into scala primarily on how to build DSL similar to C# LINQ/SQL. Having worked with C# LINQ Query provider, it was easy to introduce our own custom query provider which translated LINQ query to our own proprietary data store scripts. I am looking something similar in scala for eg.
val query = select Min(Close), Max(Close)
from StockPrices
where open > 0
First of all is this even possible to achieve in scala using internal DSL.
Any thoughts/ideas in this regard is highly appreciated.
I am still new in scala space, but started looking into Scala MetaProgramming & Slick. My complaint with Slick is i want to align my DSL close to SQL query - similar to above syntax.
There is no way to have an internal DSL (with the currently release) that looks exactly like the example you provided.
Using a macro I still had from this answer, the closest I could get (relatively fast) was:
select(Min(StockPrices.Open), Max(StockPrices.Open))
.from(StockPrices)
A real solution would take quite some time to create. If you are willing to do that you could come quite far using macro's (not a simple topic).
If you really want the exact same syntax I recommend something like XText that allows you to create a DSL with an eclipse based editor for 'free'.
The code required for the above example (I did not include the mentioned macro):
trait SqlElement {
def toString(): String
}
trait SqlMethod extends SqlElement {
protected val methodName: String
protected val arguments: Seq[String]
override def toString() = {
val argumentsString = arguments mkString ","
s"$methodName($argumentsString)"
}
}
case class Select(elements: Seq[SqlElement]) extends SqlElement {
override def toString() = s"SELECT ${elements mkString ", "}"
}
case class From(table: Metadata) extends SqlElement {
private val tableName = table.name
override def toString() = s"FROM $tableName"
}
case class Min(element: Metadata) extends SqlMethod {
val methodName = "Min"
val arguments = Seq(element.name)
}
case class Max(element: Metadata) extends SqlMethod {
val methodName = "Max"
val arguments = Seq(element.name)
}
class QueryBuilder(elements: Seq[SqlElement]) {
def this(element: SqlElement) = this(Seq(element))
def from(o: Metadata) = new QueryBuilder(elements :+ From(o))
def where(element: SqlElement) = new QueryBuilder(elements :+ element)
override def toString() = elements mkString ("\n")
}
def select(args: SqlElement*) = new QueryBuilder(Select(args))
trait Column
object Column extends Column
object tables {
object StockPrices$ {
val Open: Column = Column
val Close: Column = Column
}
val StockPrices = StockPrices$
}
And then to use it:
import tables._
import StockPrices._
select(Min(StockPrices.Open), Max(StockPrices.Open))
.from(StockPrices)
.toString
That is an admirable project, but one that has been embarked upon and which is available in general release.
I'm talking about Slick, of course.
If Scala / Java interoperability is not too much of an issue for you, and if you're willing to use an internal DSL with a couple of syntax quirks compared to the syntax you have suggested, then jOOQ is growing to be a popular alternative to Slick. An example from the jOOQ manual:
for (r <- e
select (
T_BOOK.ID * T_BOOK.AUTHOR_ID,
T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4,
T_BOOK.TITLE || " abc" || " xy"
)
from T_BOOK
leftOuterJoin (
select (x.ID, x.YEAR_OF_BIRTH)
from x
limit 1
asTable x.getName()
)
on T_BOOK.AUTHOR_ID === x.ID
where (T_BOOK.ID <> 2)
or (T_BOOK.TITLE in ("O Alquimista", "Brida"))
fetch
) {
println(r)
}