Lesson 15 - Diary with a database in Kotlin (finishing)

In the previous lesson, Diary with a database in Kotlin, we programmed a part of a digital diary using List. In today's tutorial of our Kotlin course, we're going to finish it.

Let's extend our Diary class. We'll add an auxiliary readLocalDateTime() method which will ask the user for the date and time and return a LocalDateTime instance set to this value. Have a look at it first, we'll describe it right away:

private fun readLocalDateTime(dateTimeFormatter: DateTimeFormatter): LocalDateTime {
    try {
        return LocalDateTime.parse(readLine(), dateTimeFormatter)
    } catch (e: Exception) {
        println("Incorrect date format. Please try again.")
        return readLocalDateTime(dateTimeFormatter)
    }
}

The method returns a LocalDateTime instance and takes a formatter as a parameter. In the try block, we try to parse a date and time entered by the user using the parse() method. If any error occurs due parsing the value (e.g. February 30 or something like that), the program falls into the catch block, prints an error message, and calls the method again. Calling the same method we're into is called recursions. It's a quite common programmer technique.

You might want to keep this method, it can be useful for entering date and time in your other console applications. We'll get back to try-catch blocks and talk about them in depth later once we get to working with files.

We'll add 2 public companion objects (static properties). These will be date and time formatters. We make them static to make them available for other classes in our application as well. Every class can use those fields and we have them all at one place:

companion object {
    val dateTimeFormatter = DateTimeFormatter.ofPattern("d.M.yyyy H:m")
    val dateFormatter = DateTimeFormatter.ofPattern("d.M.yyyy")
}

The first formatter contains both date and time, the second one contains only a date. Let's move to the Entry.kt class and modify the toString() method like this:

override fun toString(): String {
    return "${dateTime.format(Diary.dateTimeFormatter)} $text"
}

If we created a formatter instance every time in every Entry, it wouldn't be very practical. Using static properties, we got them all at one place and if we ever decided to change the format, we do it only in the Diary class. However, be careful with static members, it's usually not a good idea to use them.

Let's return to the Diary class and add 2 methods for retrieving date and time:

private fun readDateTime(): LocalDateTime {
    println("Enter the date and time as mm/dd/yyyy hh:mm")
    return readDateTime(dateTimeFormatter)
}

private fun readDate(): LocalDate {
    println("Enter date as mm/dd/yyyy")
    return readLocalDateTime(dateFormatter).toLocalDate()
}

They both use our readLocalDateTime() method but pass a different formatter. We'll enter date and time using readDateTime() and only date using readDate(). Notice that the second method returns just LocalDate. It'll come in handy when filtering entries by date. We surely won't need printing an entry of a particular hour and minute, however we'll print entries of a certain date without specifying the time.

Let's add a printEntries() method that will find entries for the given day and print them:

fun printEntries(day: LocalDateTime) {
    val entries = database.findEntries(day, false)

    for (entry in entries) {
        println(entry)
    }
}

We'll also need a method for prompting the user to enter a new entry and adding it to the database:

fun addEntry() {
    val dateTime = readDateTime()
    println("Enter the entry text:")
    val text = readLine()!!
    database.addEntry(dateTime, text)
}

Also, we still have to add methods for searching and deleting entries. The searching method will return a List of found entries (we'll only search by date, the time doesn't matter). We'll prompt the user to enter the date and pass it to the database. Then we'll print the result.

fun searchEntries() {
    // Entering the date
    val dateTime = readDate().atStartOfDay()
    // Searching for entries
    val entries = database.findEntries(dateTime, false)
    // Printing entries
    if (entries.size > 0) {
        println("Entries found: ")
        for (entry in entries) {
            println(entry)
        }
    } else {
        // Nothing found
        println("No entries were found.")
    }
}

Deleting entries is trivial:

fun deleteEntries() {
    println("Entries with the same exact date and time will be deleted")
    val dateTime = readDateTime()
    database.deleteEntries(dateTime)
}

Last of all, we'll add a method for printing a home screen, showing the current date and time and entries for today and tomorrow.

fun printHomeScreen() {
    println()
    println()
    println("Welcome to your virtual diary!")
    val today = LocalDateTime.now()
    println("Today is: ${LocalDateTime.now().format(dateTimeFormatter)}")
    println()
    // printing the home screen
    println("Today:\n------")
    printEntries(today)
    println()
    println("Tomorrow:\n---------")
    val tommorow = LocalDateTime.now().plusDays(1)
    printEntries(tommorow)
    println()
}

Now we can proudly move to Main.kt and create an instance of the diary. We'll also add the main loop here including a response to the user's choice:

fun main(args: Array<String>) {

    val diary = Diary()
    val choice = ""

    // main loop
    while (choice != "4") {
        diary.printHomeScreen()
        println()
        println("Choose an action:")
        println("1 - Add an entry")
        println("2 - Search for entries")
        println("3 - Delete entries")
        println("4 - End")
        choice = readLine()!!
        println()
        // reaction to the choice
        when (choice) {
            "1" -> diary.addEntry()
            "2" -> diary.searchEntries()
            "3" -> diary.deleteEntries()
            "4" -> println("Press any key to quit the program...")
            else -> println("Error. Press any key to choose another action.")
        }
    }
}

The code above isn't complicated at this point since we've already made similar applications in previous lessons. I gave the finalized app to my girlfriend as a present, here's what happened :) :

Welcome to diary!
Today is: 5/12/2016 11:34:55

Today:
-----
5/12/2016 10:00:00 Shopping - Pankrac Arcade
5/12/2016 7:30:00 PM - Pet my Yorkshire Terrier Fred

Tomorrow:
--------
5/13/2016 2:00:00 PM Go jogging


Choose an action:
1 - Add an entry
2 - Search for entries
3 - Delete entries
4 - End
2
Enter date and time as e.g. [1/13/2016 10:00]
5/15/2016
Found entries:
5/15/2016 9:30:00 Economy exam

Now you know how to use List and rest assured that it'll suffice as data storage container for a quite a long time. In conclusion, I'd like to congratulate you in now being able to create in-memory databases. Use it for anything you want! You could, for example, use it for the User class from the Properties in Kotlin or pretty much anything else. You can store articles, tasks, elephants, anything you want to manage in the database.

What's up next, you may ask? Next time, in Interfaces in Kotlin, we'll be talking about interfaces. There's still a lot to learn about OOP :)


 

 

Article has been written for you by Samuel Kodytek
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
I'm intereseted in JVM languages and Swift
Previous article
Diary with a database in Kotlin
All articles in this section
Object-oriented programming in Kotlin
Thumbnail
Next article
Interfaces in Kotlin
Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!