Supercharge Your Workflow: A Step-by-Step Guide to Advanced File Templates

Creating Custom Room Database Templates with IntelliJ: A Comprehensive Guide for Android Developers

Elevate Your Android Development Workflow with Custom File Templates: A Room Database Walkthrough

Marc Picone
by MWM

--

Introduction

Welcome to the third article in our series on using IntelliJ file templates. In the first article, we learned how to customise file and folder paths to our liking, and in the second, we explored how to use this customisation to quickly create an MVP structure for an Android app.

Now, in this article, we’ll take things even further and learn how to use file templates to create a Room database in Android Studio. But don’t worry, the goal here is not just to create a database — it’s to show you how to master file templates and leverage their power to automate repetitive tasks and speed up your development process.

So let’s dive in and learn how to take your file template game to the next level!

Link

Articles:

Gists:

Template ZIP file:

1. Room Database

In this section, we’ll create a custom file template to create the Room database with our preferred package structure, entity imports, and DAO declarations.

Let’s start by creating a new file template and naming it RoomDatabase. We want the Room database to be in a package named data/my_package_room_database/MyPackageRoomDatabase. We’ll use the NameToCamelCase template (Created in the second article) to convert the file name to camel case.

In the body of the template, we’ll start with the usual package declaration:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}.$NAME
#end

Now, we’ll fill in the necessary imports:

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

We want to be able to have the entity and Dao imports already filled in the template. To do this, we’ll create a custom variable that will prompt the user for the entities they want to use in the database. We’ll assume that the entered value will be a comma-separated list of Entity names.

To create the custom variable, we’ll jump to the top of the template, before the package declaration, and enter the following code:

#set($entitiesArray = ${StringUtils.split($ENTITIES_LIST,",")})
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ....

This sets the value of $entitiesArray by splitting the $ENTITIES_LIST on the comma. Since $ENTITIES_LIST is not set, it will be prompted by the IDE when the template is created.

Now, we can use our list of entities to import their respective Dao and DTO classes. Here’s what that looks like:

#foreach($entity in $entitiesArray )
#set($ARG1 = "$entity")
import ${PACKAGE_NAME}.data.#parse("ArgToSnakeCase.kt")_repository.${ARG1}Dao
import ${PACKAGE_NAME}.domain.#parse("ArgToSnakeCase.kt").${ARG1}DTO
#end

Here, we’re using the ArgToSnakeCase template to convert the entity names to snake case for use in the package names. This is a simple include template that uses the removeAndHump method. We’re also using the parse directive to include an undefined variable named $ARG1 that allows us to set a name to pass into the removeAndHump method.

Let’s create the ArgToSnakeCase template. We’ll create a new child template in the include tab, name it ArgToSnakeCase, and enter the following:

Now, we’ll create another include template to declare the entities. We’ll call this template RoomDbEntitiesDeclaration. In the template body, we’ll declare the entities by using the StringUtils.capitalizeFirstLetter method to capitalise the entity names and concatenate them with a hardcoded “DTO::class”.

Now, let’s use this template in our RoomDatabase file template. Here’s what the template looks like now:

Here, we’re using the Room database annotation to declare our database with the list of entities that we created in the RoomDbEntitiesDeclaration template. We’re also using the ArgToSnakeCase template to convert the entity names to snake case for use in the package names.

We could also see ArgToLowerCamelCase that is used to format variable name. Let’s create it in the include tab.

That’s it for the Room database file template!

2. Entity.kt

To create a new file template for entities, we’ll call it RoomEntity and set its extension to .kt. We want the entity to be in a package named domain/entity_name/EntityName, so the file name should be:

domain/${NAME}/#parse("NameToCamelCase.kt")

In the body of the template, we’ll define the package and class declaration.

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}.domain.${NAME}
#end
#parse("File Header.java")
data class #parse("NameToCamelCase.kt")(
#formatArgumentListForDataClass()
)

In order to populate our Entity, we will use a similar workaround as before and create a custom variable that we assume will be a list of (arg : type). This is where we introduce Macros, which are used to create functions in the VTL world. Macros will be used here to format the name of the arguments over the snippet. Let’s take a look at the macro code:

#macro( formatArgumentListForDataClass )
#set($argumentType = "")
#set($foreach = "")
#foreach($argumentType in ${StringUtils.split($ARGUMENT_LIST, ",")})
val ${argumentType} #if($foreach.hasNext()),
#end
#end
#end

Look at the way I add a coma and return to the Line if the list is not ended. The formatArgumentListForDataClass macro will format the argument list correctly, adding commas and returning to a new line if the list is not yet ended.

Now, let’s create a function to map the Entity to DTO. We’ll use a macro called formatArgumentListForDTOMapper to format the argument list.

#macro( formatArgumentListForDTOMapper )
#set($argumentType = "")
#set($argumentNameArray = [])
#set($foreach = "")
#foreach($argumentType in ${StringUtils.split($ARGUMENT_LIST, ",")})
#set($argumentNameArray = ${StringUtils.split($argumentType, ":")})
$argumentNameArray.get(0) = this.$argumentNameArray.get(0) #if($foreach.hasNext()),
#end
#end
#end

With this macro, we’ll create the entity body as follows:

{
fun toDTO(): #parse("NameToCamelCase.kt")DTO{
return #parse("NameToCamelCase.kt")DTO(
#formatArgumentListForDTOMapper()
)
}

We can now create a DTO template in a child template to our entity. We’ll set the file name to :

domain/${NAME}/#parse("NameToCamelCase.kt")DTO

and set its extension to .kt.

In the body of the template, we’ll define the package and class declaration, the tableName, and use the formatArgumentListForDataClass and formatArgumentListForModelMapper macros to create the argument list and the function to map the model to the DTO.

Now that we have our entity created, it’s time to move on to the next step in our journey towards mastering file templates in IntelliJ — creating the repository.

3. Repository.kt

In this section, we will create a new include template called NameToLowerCamelCase that converts the variable $NAME to lower camel case. We will use this include template to create a new child template for our entity’s repository. The repository will have all CRUD methods, and we will also create an implementation of the repository.

First, let’s create the NameToLowerCamelCase include template.

Now, let’s create a new child template for our entity’s repository. We want the file name to be data/package_name_repository/PackageNameRepository, so the file name is:

data/${NAME}_repository/#parse("NameToCamelCase.kt")Repository

The body will be:

And that’s done.

Now let’s create the implementation file.

The file name will be:

data/${NAME}_repository/#parse("NameToCamelCase.kt")RepositoryImpl

And the body:

In summary, we created a new include template NameToLowerCamelCase to convert the variable $NAME to lower camel case. We used this template to create a new child template for our entity’s repository, with all CRUD methods. Finally, we created an implementation of the repository, using the previously created child template and the necessary imports.

And the last, the DAO

The file name is:

data/${NAME}_repository/#parse("NameToCamelCase.kt")Dao

And the body :

This DAO file template defines methods to insert, query, update, and delete items in the database. It also includes the Dao annotation required by Room, as well as a companion object to define the table name.

Feel free to modify this template to suit your needs. Remember, the goal of this series of articles is to give you a step-by-step approach to deep diving into file templates, so don’t be afraid to experiment and try new things!

Conclusion

In conclusion, file templates can be incredibly useful for streamlining repetitive tasks in Android development, and we’ve seen how they can be used to create custom formatting for paths and generate complex views with multiple files. In this third article, we explored how to use file templates to generate code for a Room database, showing that the possibilities for template creation are only limited by one’s creativity.

While we’ve focused on specific use cases in this series of articles, it’s important to remember that file templates can be applied to a wide range of scenarios in Android development. They can help save time and reduce errors by automating repetitive tasks and allowing developers to focus on the more creative aspects of their work.

I hope that these articles have helped demystify file templates and encouraged you to explore their potential in your own projects.

Happy coding!

--

--

Marc Picone
by MWM
Writer for

Hello! I'm Marc, Android developer at MWM. I've been creating innovative music, drawing, and creative apps with MWM since 2021. Let's connect and exchange ideas