Chained Variables / Anchor Variable and Error: Only a chained variable supports a singleton inverse - kotlin

Preface: Google+SO+docu search did not appear to give relevant information.
Domain Model:
My domain model tries to picture an ProductionPlan, containing a List of Machines.
Each Machine has a list of chained jobs, thus a job which has a getNextEntry():Job Method, creating a list of jobs.
I tried to solve this problem with chained PlanningVariables, but apparently do not understand the concept of chainedVariables/shadowVariables/anchorVariables.
With my understanding, all jobs are getting chained and the anchorShadowVariable points to the beginning of the list, thus the machine.
To implement chaining, Job and Machine need to implement an interface or extend superclass, thus i created ChainSuperClass. Additionally i do not know if i have to overwrite getter/setter to set Annotations in Machine-Class, my guess was that because Machine extends ChainSuperClass, those Annotations carry over.
Edit: Kotlin specific improvements are also appreciated.
The full error log of my code execution is now:
Exception in thread "main" java.lang.IllegalArgumentException: The entityClass (class optaplanner.productionPlan.domain.ChainSuperClass) has a InverseRelationShadowVariable annotated property (nextEntry) which does not return a Collection with sourceVariableName (machine) which is not chained. Only a chained variable supports a singleton inverse.
ChainSuperClass:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
#InverseRelationShadowVariable(sourceVariableName = "machine")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
Job:
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
#AnchorShadowVariable(sourceVariableName = "machine")
var machine: Machine? = null
private var nextEntry: Job? = null
#PlanningVariable(
valueRangeProviderRefs = ["jobList"],
graphType = PlanningVariableGraphType.CHAINED
)
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Machine:
class Machine(override val id: String, val jobTypes: List<JobType>) : ChainSuperClass() {
private var nextEntry: Job? = null
override fun setNextEntry(job: Job) {
this.nextEntry = job
}
override fun getNextEntry(): Job? {
return nextEntry!!
}
}

I think the most important thing to realize with chained variables is this: When you have an entity, say Job A and solver assigns a value (job/machine) to its variable, it's not like the chain is being built forward starting from Job A. It's the other way around. By assigning a value to Job A's planning variable, Job A gets connected at the end of an existing chain.
Please take a look at the documentation to find more details about chaining and examples of valid chains.
By having understood this it should be clear that Job's planning variable name should be something like previousJobOrMachine (you'll probably want something simpler, for example previousStep) whereas the nextJob property is an inverse relation shadow variable derived from that (so when Job X gets connected to an existing chain ending with Job C by assigning Job X.previousStep=Job C, an inverse relation is established automatically: Job C.nextJob=Job X).
Based on that information, your model should look more like this:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
// changed sourceVariableName to point to the planning variable
#InverseRelationShadowVariable(sourceVariableName = "previousStep")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
// changed sourceVariableName to point to the planning variable
#AnchorShadowVariable(sourceVariableName = "previousStep")
var machine: Machine? = null
// added planning variable
private var previousStep: ChainSuperClass? = null
private var nextEntry: Job? = null
#PlanningVariable(
// added machineList value range provider
valueRangeProviderRefs = ["jobList", "machineList"],
graphType = PlanningVariableGraphType.CHAINED
)
// getter for the new planning variable
fun getPreviousStep(): ChainSuperClass {
return previousStep
}
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Notice that I added machineList as the source of possible values of the previousStep planning variable as previous step might be either a Job at the end of a non-empty chain or a Machine representing an empty chain.
Your domain is very similar to the vehicle routing domain, where you can see a working example of chaining. With good understanding of chaining principles you should be able to replicate it on your domain.

Related

Is there a way to make the first digit of int always start with 1 in Kotlin

Let's say I have the following class constructor:
class Car(val brand: Brand,val modelName: String, val version: Int){}
If for example, I want the version number to always start with 1. Is there a way to manipulate it in the class body to achieve this ?
Meaning:
val firstdigit:Int = abs(version).ToString().Substring(0,1)
And then parse it to Int. But how to replace the original first digit after that?
I'm just learning Kotlin and I got a bit stuck with this
Is this what you had in mind?
class Car(val brand: Brand, val modelName: String) {
val version = getNextVersion()
companion object {
private var nextVersion = 0
private fun getNextVersion(): Int {
nextVersion++
if (nextVersion.toString()[0] != '1') {
nextVersion = (10.0.pow(ceil(log10(nextVersion.toDouble())))).toInt()
}
return nextVersion
}
}
}
You already said in the comments that you want the number to increment per instance, so the caller shouldn't be providing that number in the first place really! But just generally, here's two approaches to sanitising your input parameters:
1) Make it the caller's responsibility to provide valid data
init {
require(version.toString().first() == '1') { "Needs to start with 1 thanks" }
}
require throws an IllegalArgumentException if it fails, which is the standard exception for "the value of this argument is invalid". Should the class be responsible for taking bad data and trying to "fix" it, or should the caller be handling that - and maybe not constructing an instance at all if it doesn't have valid data?
2. create a newInstance function that uses valid data, and keep the constructor private
class Thing private constructor(val number: Int){
companion object {
fun newInstance(num: Int): Thing {
return Thing(abs(num))
}
}
}
fun main() {
Thing.newInstance(-2).let { println(it.number)}
}
If it makes sense for the class itself to sanitise the input parameters, you can delegate construction to a function that takes care of that, and prevent things from calling the constructor directly with potentially bad data.
This can cause issues with e.g. serialisation libraries (which want to call the constructor directly) but in that case you could leave the constructor public, and just advise callers to call newInstance instead. Not ideal, but it's an option!

Kotlin, implement extension var property

It seems that a simple extension property like the following does not work.
var Dog.age = 0;
What is the recommended way to implement this? I have tried the following, and it worked, but this will prevent any Dog object from cleaned up by the Garbage Collector, won't it?
class Dog
{
}
val dogAgeMap=HashMap<Dog, Int>();
var Dog.age:Int
get() = dogAgeMap[this]?: 0;
set(value){ dogAgeMap[this] = value}
class PetShop
{
fun work()
{
val d1 = Dog();
d1.age = 100;
val d2 = Dog();
d2.age = 200;
println("${d1.age}, ${d2.age}");
}
}
fun main(args:Array<String>)
{
PetShop().work();
}
Correct, this will prevent the Dog instances on which the age setter has been called to be GCed inside the scope of where the dogAgeMap is defined. If you defined the Dog.age extension property (and thus dogAgeMap) in a limited scope with a limited (short) lifespan, then you are okay.
However, if that is not the case, and you need the age info all across you application, then age should just be part of the original class definition and you don't ever run into this problem.
Solution in this case
class Dog(val age: Int)
If you need the age information only in one part of your application, then a better way would be to create the lookup (the HashMap) only for that part, or to simply use an enriched class with age (or a wrapper class with age) instead of the Dog class in that part of your application. And when you are done with work there, you clean up the map or the enriched class instances. In that way no instances will leak.
But if you really really want to do it with an extension property across the whole application, and thus you need to keep the reference to the dogAgeMap all the time, then you need to take care of leaking memory if you have a lot of instances that you go through and set their age.
If that is your case you can use a WeakHashMap<Dog, Int> instead. A WeakHashMap only keeps weak references and it won't prevent Dog instances to be GCed (once your strong references are no longer retained).
import java.util.WeakHashMap
val dogAgeMap = WeakHashMap<Dog, Int>()
var Dog.age: Int
get() = dogAgeMap[this] ?: 0
set(value) {
dogAgeMap[this] = value
}
Note however, that WeakHashMap is a Java class and not part of Kotlin core library, so if you use Kotlin for multiplatform, this won't work. In that case you would need a WeakHashMap implementation (library) on each platform.
An alternative way to do this if your data for dogs also contains an ID for each dog, would be to use the ID as the lookup key instead. That would be possible to port to all platforms. The implementation would then change to
// I am using a Long here, but it could be whatever type that
// is small enough to not cause memory concerns, since
// these keys would still exist in memory because a normal HashMap is used.
class Dog(val id: Long) {}
val dogAgeMap = HashMap<Long, Int>()
var Dog.age: Int
get() = dogAgeMap[id] ?: 0
set(value) {
dogAgeMap[id] = value
}

Android ROOM, insert won't return ID with first insert, but will return with 2nd insert onwards

School project and I'm pretty new to Android development.
The problem
I have a button with a onClick listener in a save person fragment which will save person data to the database. Everything works fine except for some reason with the first click it wont return me the inserted row ID but it will do so with 2nd click onwards.
I really need this ID in before proceeding to the next fragment.
Not sure if this is important but whenever I return (reload) to this save person fragment, the behaviour is always the same that the first click allways fails to capture the inserted row ID.
input data:
first name = John
last name = Smith
Just for demo purpose, if I will try to use this button 3x to insert the person data (returned insert ID is in the log), I will get all 3 rows in database with name John Smith, but the very first inserted row ID is not captured (default initialised value is 0), please see the log below:
Log
2020-10-19 12:49:20.320 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 0
2020-10-19 12:49:40.153 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 5
2020-10-19 12:49:40.928 25927-25927/ee.taltech.mobile.contacts D/TEST_ADD_PERSON_ID: insertedPersonId: 6
EDITED ORIGINAL post
As suggested in the comments, I'm trying to go about the way of using LiveData and observer, but I'm still little bit stuck.
The setup
The below is the current setup.
Entity
#Entity(tableName = "person")
data class Person(
#PrimaryKey(autoGenerate = true)
val id: Int,
DAO
#Dao
interface PersonDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addPerson(person: Person): Long
Repository
class PersonRepository(private val personDao: PersonDao) {
val readAllPersonData: LiveData<List<Person>> = personDao.readAllPersonData()
suspend fun addPerson(person: Person): Long {
return personDao.addPerson(person)
}
ViewModel
I'm not sure if I'm doing things right at all here. I broke it down here in steps and created separate variables insertedPersonILiveData and insertedPersonId.
How could pass the returned row id to insertedPersonILiveData?
class PersonViewModel(application: Application) : AndroidViewModel(application) {
var insertedPersonILiveData: LiveData<Long> = MutableLiveData<Long>()
var insertedPersonId: Long = 0L
val readAllPersonData: LiveData<List<Person>>
private val repository: PersonRepository
init {
val personDao = ContactDatabase.getDatabase(application).personDao()
repository = PersonRepository(personDao)
readAllPersonData = repository.readAllPersonData
}
suspend fun addPerson(person: Person) = viewModelScope.launch {
insertedPersonId = repository.addPerson(person)
// ****************************************************************
// insertedPersonILiveData = insertedPersonId (what to do here) ???
// ****************************************************************
}
Save person fragment
This is the way I'm calling out the addPerson via modelView.
val person = Person(0, firstName, lastName)
lifecycleScope.launch {
personViewModel.addPerson(person)
}
Log.d("TEST_ADD_PERSON_ID","insertedPersonId: ${personViewModel.insertedPersonId}")
And this is the way I have done the observer (not sure if it's even correct).
val returnedIdListener: LiveData<Long> = personViewModel.insertedPersonILiveData
returnedIdListener.observe(viewLifecycleOwner, Observer<Long> { id: Long ->
goToAddContactFragment(id)
})
private fun goToAddContactFragment(id: Long) {
Log.d("TEST_ADD_PERSON_ID", "id: " + id)
}
Create database
#Database(
entities = [Person::class, Contact::class, ContactType::class],
views = [ContactDetails::class],
version = 1,
exportSchema = false
)
abstract class ContactDatabase : RoomDatabase() {
abstract fun personDao(): PersonDao
abstract fun contactTypeDao(): ContactTypeDao
abstract fun contactDao(): ContactDao
abstract fun contactDetailsDao(): ContactDetailsDao
companion object {
// For Singleton instantiation
#Volatile
private var instance: ContactDatabase? = null
fun getDatabase(context: Context): ContactDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
private fun buildDatabase(context: Context): ContactDatabase {
return Room.databaseBuilder(context, ContactDatabase::class.java, "contacts_database")
.addCallback(
object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
WorkManager.getInstance(context).enqueue(request)
}
}
)
.build()
}
}
}
You're starting a coroutine to run addPerson, and then immediately calling Log with the current value of insertedPersonId in the viewmodel. The coroutine will run, insert the person, and update the VM with the ID of the inserted row, but that will happen long after your Log has run. Probably all of your results are actually the ID of the last record that was inserted.
I'm new to a lot of this too, but just based on what you have now, I think you just need to add
insertedPersonILiveData.value = insertedPersonId
in your addPerson function. That way you're updating that LiveData with a new value, which will be pushed to any valid observers. You've written some code that's observing that LiveData instance, so it should get the update when you set it.
edit your problem is that insertedPersonILiveData is the immutable LiveData type, so you can't set the value on it - it's read-only. You're creating a MutableLiveData object but you're exposing it as a LiveData type.
The recommended pattern for this is to create the mutable one as an internal object, expose a reference to it as an immutable type, and create a setter method that changes the value through the mutable reference (which it can access internally)
class myViewModel : ViewModel() {
// mutable version is private, all updates go through the setter function
// (the _ prefix is a convention for "private versions" of data fields)
private val _lastInsertedPersonId = MutableLiveData<Long>()
// we're making the instance accessible (for observing etc), but as
// the immutable LiveData supertype that doesn't allow setting values
val lastInsertedPersonId: LiveData<Long> = _lastInsertedPersonId
// setting the value on the MutableLiveData instance
// is done through this public function
fun setLastInsertedPersonId(id: Long) {
_lastInsertedPersonId.value = id
}
}
and then your observer would just call lastInsertedPersonId.observe, you don't need to copy the LiveData and observe that (like you're doing with returnedIdListener.
That's the basic pattern right there - internal MutableLiveData, exposed publicly as an immutable LiveData val, with a setter method to update the value. Everything outside the view model either observes the LiveData that's visible, or calls the setter method to update. Hope that makes sense! It's not that complicated once you get your head around what's basically going on

In Kotlin, how do I create an instance of a class where the class name is a variable?

I am writing a Graph class in Kotlin. Here's my code so far:
import java.util.*
class Graph<Class>(connectionProb: Double) {
// a general network class
val nodes = mutableListOf<Class>()
val connectionProb = connectionProb
val connections = hashMapOf<Class, MutableList<Class>>()
var nextID = 0
private fun createNode() {
// Here is where I need help
}
}
I want to be able to specify the node type for each Graph. The code above will compile, but I'm unsure how to initialize new nodes. The only argument I want to pass into each node, regardless of its type, is nextID.
Thanks!
First thing you should do is define a class for your graph node, and instead of maintaining a map for connections, let every node maintain a list of its connections.
So your code would look like this
class Graph<T>(connectionProb: Double) {
var nextId = 0
val nodes = mutableListOf<Node<T>>()
// Define a node class, so if you have graph of Integer then you want a node of Integer
class Node<T>(var id: Int){
val connections = mutableListOf<Node<T>>()
fun addConnection(newConnection: Node<T>){
connections.add(newConnection)
}
override fun toString() = "$id"
/** add other functions, such as remove etc */
}
fun createNode(){
var newNode = Node<T>(nextId++)
nodes.add(newNode)
}
}
Now to use this graph you will do followig
fun main(){
var myGraph = Graph<Integer>(10.9)
myGraph.createNode()
myGraph.createNode()
myGraph.nodes[0].addConnection(myGraph.nodes[1])
/* Print all nodes in graph */
for(node in myGraph.nodes) print(" $node")
/* Print connections of first node */
for(node in myGraph.nodes[0].connections) print("\n\nConnection id: $node")
}
Please note that this will give you a general idea of how to go about implementing graph and there are many things that you can improve in the implementation. To learn more I suggest you read a good book, such as Data Structures and Algorithms in Java

Retrieve data class members

I need to check if any variables inside of my data class are null. To do this I need retrieve them first but I can't access them directly (e.g. myDataClass.name) because I need it to be generic. Is there a way to access these variables without directly naming them. For example, like accessing a member of an array (myArray[0]).
The mechanism you're looking for is called "reflection" and it allows to introspect objects at runtime. You'll find a lot of information on the internet, but just to give you a link you may want to check this answer.
In your case you could do something like this:
data class MyDataClass(
val first: String?,
val second: String?,
val third: Int?
)
fun main() {
val a = MyDataClass("firstValue", "secondValue", 1)
val b = MyDataClass("firstValue", null, null)
printProperties(a)
printProperties(b)
}
fun printProperties(target: MyDataClass) {
val properties = target::class.memberProperties
for (property in properties) {
val value = property.getter.call(target)
val propertyName = property.name
println("$propertyName=$value")
}
}
Note that for this code to work you must add kotlin-reflect package as a dependency.