Convert kotlin annotation to constant - kotlin

I have written my custom annotation processor for my bot command system.
It looks like this:
#Command(
name = "User profile",
description = "Fetches user profile",
identifiers = ["user", "u", "гыук", "г"]
)
class UserProfileCommand (
#Argument(
name = "Player",
description = "Something written here",
identifiers = ["nick", "nickname", "тшсл", "тшслтфьу"],
required = true,
implicit = true
)
val actor: String,
#Argument(
name = "Server",
description = "Something written here",
identifiers = ["server", "s", "ыукмук", "ы"],
)
#Default("bancho")
val server: String,
#Argument(
name = "Mode",
description = "Something written here",
identifiers = ["mode", "m", "ьщву", "ь"],
)
#Default("default")
val mode: String,
#Argument(
name = "Render mode",
description = "Enables render mode",
identifiers = ["render", "r", "кутвук", "к"]
)
#Default("false")
val isRenderMode: Boolean
The problem is that I want to reuse some definitions of the arguments in other commands, so I want to move annotations to constants and use them.
object Arguments {
val Actor = Argument(
name = "Actor",
description = "",
identifiers = arrayOf("nick", "nickname", "тшсл", "тшслтфьу"),
required = true,
implicit = true
)
}
I can create instance of Argument like that and move it to constant but I cant find any way to use it. The example below unsurprisingly gives me error:
#Argument = Arguments.Actor
val actor: String,
Is there any way to achieve what I want? Is there any way I can to it without using code generation libraries? If I need to use these libraries, then which one can you recommend? How can I generate annotations with it?

Related

How can we dynamically generate a list of map in terraform?

I have a list of rules which i want to generate at runtime as it depends on availability_domains where availability_domains is a list
availability_domains = [XX,YY,ZZ]
locals {
rules = [{
ad = XX
name = "service-XX",
hostclass = "hostClassName",
instance_shape = "VM.Standard2.1"
...
},{
ad = YY
name = "service-YY",
hostclass = "hostClassName",
instance_shape = "VM.Standard2.1"
...
}, ...]
}
Here, all the values apart from ad and name are constant. And I need rule for each availability_domains.
I read about null_resource where triggers can be used to generate this but i don't want to use a hack here.
Is there any other way to generate this list of map?
Thanks for help.
First, you need to fix the availability_domains list to be a list of strings.
availability_domains = ["XX","YY","ZZ"]
Assuming availability_domains is a local you just run a forloop on it.
locals {
availability_domains = ["XX","YY","ZZ"]
all_rules = {"rules" = [for val in local.availability_domains : { "ad" : val, "name" : "service-${val}" , "hostclass" : "hostClassName", "instance_shape" : "VM.Standard2.1"}] }
}
or if you dont want the top level name to the array then this should work as well
locals {
availability_domains = ["XX","YY","ZZ"]
rules = [for val in local.availability_domains : { "ad" : val, "name" : "service-${val}" , "hostclass" : "hostClassName", "instance_shape" : "VM.Standard2.1"}]
}

Kotlin: class instance variable value-parameter error

First post - New to Kotlin so beginner-learner. Please be gentle if my terminology is not quite up to scratch yet!
I'm attempting to call a parameter that i've declared in a secondary constructor within my main function variable but it doesnt format like the primary constructor variables and doesn't let me initialise a value that can then be called like the others.
Problem line: (it's the 'age =' bit)
var phoneTwo = MobilePhone("Apple", "iphone", "X", age = )
full syntax below:
fun main() {
var phoneTwo = MobilePhone("Apple", "iphone", "X", age = )
var phoneOne = MobilePhone("Samsung", "Galaxy", "S20",)
println("What is your hobby?: ")
phoneOne.hobby = readLine().toString()
phoneOne.stateHobby()
phoneTwo.hobby = "Plumbing"
phoneTwo.stateHobby()
phoneTwo.age = 32
println("PhoneTwo is $phoneTwo.age years old")
}
class MobilePhone(osName: String, brand: String, model: String) {
// member variables - Properties - variables within a class
var age : Int? = null
var hobby : String = "Watch Netflix"
// Initializer block
init {
println("A new mobile phone was created called $osName which is " +
"brand $brand and it's model is $model")
}
// member secondary constructor
constructor(osName: String, brand: String, model: String, age: Int):
this(osName,brand,model){
this.age = age
}
// Member function - functions within a class
fun stateHobby() {
println("Your hobby is $hobby")
}
This is about the syntax of calling a method/constructor in Kotlin, not about secondary constructors as such (which are called in exactly the same way as others).
First, let's review the syntax for calling a method (or constructor). You can just list the arguments alone (just as you do in Java, C, or many other languages), e.g.:
MobilePhone("Apple", "iphone", "X", 5)
However, Kotlin also allows you to specify the names of the parameters they're being passed to, e.g.:
MobilePhone(osName = "Apple", brand = "iphone", model = "X", age = 5)
That's more long-winded, but you may find it easier to read and safer (especially if there are multiple parameters with the same type). It also lets you put the arguments in any order, e.g.:
MobilePhone(model = "X", osName = "Apple", age = 5, brand = "iphone")
You can even mix and match the forms, as long as the unnamed arguments come first, e.g.:
MobilePhone("Apple", "iphone", age = 5, model = "X")
(This feature is only mildly useful on its own, but is very handy for a related feature: the ability to specify default values for some or all of the parameters. See that link for more.)
Hopefully this illustrates why the problem line doesn't make sense:
var phoneTwo = MobilePhone("Apple", "iphone", "X", age = )
That looks like you want to call the secondary constructor, passing the values "Apple", "iphone", and "X" to the first three parameters, and then naming another parameter but without passing it a value. This is of course a syntax error.
If you have a value to pass for the age, then just pass it, either with the parameter name:
var phoneTwo = MobilePhone("Apple", "iphone", "X", age = 5)
or without:
var phoneTwo = MobilePhone("Apple", "iphone", "X", 5)
Or if you don't have a value, then simply call the primary constructor instead:
var phoneTwo = MobilePhone("Apple", "iphone", "X")
Incidentally, this means that your class doesn't actually need a secondary constructor at all. You could simply include the optional parameter in the primary constructor, with a default value:
class MobilePhone(osName: String, brand: String, model: String, var age: Int? = null) {
Then callers can either specify the age param, or omit it (and get the null default value).
In fact, features such as multiple constructors, method overloading, and builder classes tend to be used less in Kotlin than in some other languages, because default parameters cover their main use cases.
I'm not sure what you're trying to do exactly, but hopefully this covers it! This:
var phoneTwo = MobilePhone("Apple", "iphone", "X", age = )
is trying to call your secondary constructor, and you're using a named argument for one of the parameters (age) but you're not actually passing a value for it. If you do, it'll compile
MobilePhone("Apple", "iphone", "X", age = 3)
You don't actually need to name the argument there - if you just pass an Int as the 4th parameter, it'll match your secondary constructor's signature (number of parameters, correct types for each, in the same order), so the compiler will know that's what you're calling. If you omit it, it'll match the primary constructor. You can still keep the name there for readability, if you like.
But you can actually duplicate the functionality you have there with a default parameter, which is where you supply a value to use if the call doesn't specify one:
class MobilePhone(osName: String, brand: String, model: String, val age: Int? = null) {
// member variables - Properties - variables within a class
var hobby : String = "Watch Netflix"
// Initializer block
init {
println("A new mobile phone was created called $osName which is " +
"brand $brand and it's model is $model")
}
So now, age is a parameter on the primary constructor - if you don't supply it (just calling with the first 3 items, which are required because they don't have defaults) then it defaults to null.
By making that parameter a val (or a var if you like) it becomes a class property you can reference later, instead of only being accessible during construction. So you can remove the var age property inside the class, because this is basically the same thing (and they'll clash anyway)
And now that you have a primary constructor you can call with or without the age parameter, there's no need for the secondary one anymore! This is where named parameters really come in useful - if you had defaults for each of those params, you could just supply the specific ones you're interested in, by using their names
oh also, "PhoneTwo is $phoneTwo.age years old" won't work - if you're just referencing an object, you can do $phoneTwo, but anything more complicated (like accessing one of its properties, or any method calls or more complex expressions) have to be wrapped in ${}
println("PhoneTwo is ${phoneTwo.age} years old")
println("PhoneTwo is ${phoneTwo.age * 365} days old")

How can I loop over an array of sets/maps with varying data types

I want to make it so that I keep my code dry and create 3 (or more, or less) buttons with somewhat the same structure. So I create a list of objects to loop over and put the data inside the object to use in several places in the AppButton.
I might think a bit too Pythonic, because that's my main language and I only recently started using Kotlin. What I normally do in Python:
app_buttons = [
dict(
text="....",
icon="....",
uri_string="....",
),
...
]
I've tried something similar in Kotlin with mapOf:
val appButtons = arrayOf(
mapOf(
"title" to getString(R.string.app_btn_example1),
"icon" to R.drawable.ic_some_icon_1_64,
"uriString" to "myapp://example1",
),
...
)
and then loop over them and getting from the map:
for (entry in appButtons) {
buttons.add(
AppButton(
entry.get("text"),
entry.get("icon"),
) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(entry.get("uriString"))).apply {
val name = getString(R.string.saved_account_key)
putExtra(name, sharedPref.getString(name, null))
}
startActivity(intent)
}
)
}
But then I get Type mismatch. Required String. Found {Comparable & java.io.Serializable}?. I don't know what types to put where...
Ok different approach, using setOf and destructuring:
val appButtons = arrayOf(
setOf(
getString(R.string.app_btn_example1),
R.drawable.ic_some_icon_1_64,
"myapp://example1",
),
...
)
for ((text, icon, uriString) in appButtons) {
buttons.add(
AppButton(
text,
icon
) {
...
}
)
}
But now I get the following:
Destructuring declaration initializer of type Set<{Comparable<*> & java.io.Serializable}> must have a 'component1()' function
Destructuring declaration initializer of type Set<{Comparable<*> & java.io.Serializable}> must have a 'component2()' function
Destructuring declaration initializer of type Set<{Comparable<*> & java.io.Serializable}> must have a 'component3()' function
How do I make this work? How do I create a basic list of objects and loop over them with the correct types? It feels so simple in Python. I'm clearly missing something.
Rather than using maps, you should create a data class. For example:
data class ButtonModel(
val title: String,
val icon: Int,
val uriString: String,
)
You can then create the array like this:
val appButtons = arrayOf(
ButtonModel(
title = getString(R.string.app_btn_example1),
icon = R.drawable.ic_some_icon_1_64,
uriString = "myapp://example1",
),
...
)
Or without the parameter labels if you prefer:
val appButtons = arrayOf(
ButtonModel(
getString(R.string.app_btn_example1),
R.drawable.ic_some_icon_1_64,
"myapp://example1",
),
...
)
Then, rather than getting them with get or [], you can just use the dot syntax:
buttons.add(
AppButton(
entry.text,
entry.icon,
) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(entry.uriString)).apply {
val name = getString(R.string.saved_account_key)
putExtra(name, sharedPref.getString(name, null))
}
startActivity(intent)
}
)

How to write annotation for documentation in Kotlin?

I would like to add documentation in source code not attached to any specific Kotlin class or method, it's like conceptual, high-level documentation.
I would like to keep it in the Kotlin source file, close to things it describes.
Ideally I would like to be able to write it as:
...
doc(
title = "Some title",
text = "Some text"
)
...
But it's not possible as Kotlin won't allow top level calls, I also tried to use annotations, but it's also not allowed without attaching something to it.
#Doc(
title = "Some title",
text = "Some text"
)
So, the code below works but it's not looks nice. Is there something better?
#Doc private val doc1 = doc(
title = "Some title",
text = "Some text"
)
Or maybe
#Doc(
title = "Some title",
text = "Some text"
) val tmp = 0

Groovy: Could not find matching constructor

Can anyone help me resolve why I am getting this run time error(cannot find the matching constructor) for the following class (JobStage). I tried to create an instance of the following class but it results in the following run time error.
class JobStage {
String name
StageType stageType
String transformationType
List<ImField> fields
TableAttribute tableAttributes
List<Connector> inputConnectors
List<Connector> outputConnectors
JobStageProperties stageProperties
JobStage(String name, StageType stageType, String transformationType,
List<ImField> fields,TableAttribute tableAttributes, List<Connector> inputConnectors,
List<Connector> outputConnectors, JobStageProperties stageProperties){
this.name = name
this.stageType = stageType
this.transformationType = transformationType
this.fields = fields
this.tableAttributes = tableAttributes
this.inputConnectors = inputConnectors
this.outputConnectors = outputConnectors
this.stageProperties = stageProperties
}
}
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.starling.informatica.dto.jobstage.JobStage(String, com.starling.informatica.dto.StageType, String, ArrayList, null, ArrayList, com.starling.informatica.dto.stageProperties.SourceRelationalStageProperties)
The error here assumes the TableAttribute class as null and igonres List<Connector> outputConnectors argument
I tried to create instance like so :
JobStageProperties stageProperties = TeradataSpecUtil.getSourceStageProperties()
List<ImField> fields = [new SourceField("integer","","customer_id","NULL","","10","0","","1","NOT A KEY","0","ELEMITEM","NO","11","0","0","0","10","0",""),
new SourceField("varchar","","customer_name","NULL","","50","0","","2","NOT A KEY","0","ELEMITEM","NO","0","0","0","11","50","10","")
]
List<Connector> inputConnectors = []
List<Connector> outputConnectors = [new Connector("customer_id","informatica_customer_source","Source Definition","customer_id","SQ_informatica_customer_source" ,"Source Qualifier"),
new Connector("customer_name","informatica_customer_source","Source Definition","customer_name","SQ_informatica_customer_source" ,"Source Qualifier")
]
TableAttribute tableAttribute = null
List<JobStage> expectedJobStage= [new JobStage("informatica_customer_source",
StageType.TERADATA_CONNECTOR, "Source Definition", fields,tableAttribute, inputConnectors,
outputConnectors, stageProperties)]
My Code is logically correct, the problem was with Intellij. The code ran successfully after I performed "Invalidate Caches and Restart".