MapperJ

Additional

Language
Java
Version
N/A
Created
Sep 17, 2018
Updated
Oct 26, 2018
Owner
Александр (sashamerkulev)
Contributor
Александр (sashamerkulev)
1
Activity
Badge
Generate
Download
Source code

Blurb

MapperJ

MapperJ is an annotation processing library which helps you get rid of manual creating of mapper classes.

I hope everybody uses Clean Architecture approaches for creating theirs Android projects now. Who uses this approaches needs to write boring mapper classes which transfer data between layers, for example:

  • db source (Entities models to Domain models and back)
  • net source (Responses models to Domain models)
  • domain (Domain models to UI models)

So this annotation processing code helps you get rid of such works (different way of MapStruct).

Installation

Add to the build.gradle of your app module:

dependencies {
    implementation 'com.github.sashamerkulev:MapperJ-annotations:1.0.6'
    kapt 'com.github.sashamerkulev:MapperJ-processors:1.0.6'
    annotationProcessor 'com.github.sashamerkulev:MapperJ-processors:1.0.6'
}

Usage

This is a common case of using @Mapper annotations.

@Mapper(twoWayMapClasses = [DbEntity::class], oneWayMapClasses = [ModelResponse::class])
data class DomainModel1(
        val id: Int,
        val name: String,
        val x: Float,
        val y: Double,
        val z: Long,
        val child: DomainChild,
        val ab: Short
)

data class DomainChild(
        val id: Int,
        val name: String,
        val zz: Byte,
        val ab: Short
)

data class DbEntity(
        val id: Int,
        val name: String,
        val zz: Byte,
        val ab: Short,
        val children: List<DbChildEntity>
)

data class DbChildEntity(
        val id: Int,
        val name: String,
        val zz: Byte,
        val ab: Short
)

data class ModelResponse(
        val id2: Int,
        val name: String,
        val x: Float,
        val y: Double,
        val child: ChildResponse
)

data class ChildResponse(
        val id: Int,
        val name: String,
        val zz: Byte,
        val ab: Short
)

And this is a code which was generated by this library is based on @Mapper annotation is mentioned before:

public class DomainModel1Mapper {

    public DomainModel1 mapToDomainModel1(DbEntity item) {
        return new DomainModel1(item.getId(), item.getName(), 0F, 0.0, 0, null, item.getAb());
    }

    public DbEntity mapToDbEntity(DomainModel1 item) {
        return new DbEntity(item.getId(), item.getName(), (byte) 0, item.getAb(), new ArrayList());
    }

    public DomainModel1 mapToDomainModel1(ModelResponse item) {
        return new DomainModel1(0, item.getName(), item.getX(), item.getY(), 0, mapToDomainChild(item.getChild()), (short) 0);
    }

    public DomainChild mapToDomainChild(ChildResponse item) {
        return new DomainChild(
                item.getId(), item.getName(), item.getZz(), item.getAb()
        );
    }
}

Certainly you can use the following usage:

@Mapper(source = Source.Kotlin, twoWayMapClasses = [DbEntity::class], oneWayMapClasses = [ModelResponse::class])

In this case the library will generate Kotlin code.

Args

Args is an annotation processing library which generates a class which helps you to transfer data to Intent or Bundle and back and it is comfortable way for transfering arguments to fragments, activities, services and etc.

Usage

@Args(source = Source.Kotlin)
data class BundleModel(
    val id: Int,
    @DefaultValue(stringValue = "yes") val name: String,
    val shrt: Short,
    @DefaultValue(longValue = 334) val lng: Long,
    val bol: Boolean,
    val bte: Byte,
    @DefaultValue(floatValue = 334F) val foat: Float,
    val dbl: Double,
    @Ignore val aaa: String
)

This annotation generates the following class:

data class BundleModelArgs(
    val id: Int,
    val name: String,
    val shrt: Short,
    val lng: Long,
    val bol: Boolean,
    val bte: Byte,
    val foat: Float,
    val dbl: Double
) {
    fun toIntent(): Intent {
        val intent = Intent()
        intent.putExtra("id", id)
        intent.putExtra("name", name)
        intent.putExtra("shrt", shrt)
        intent.putExtra("lng", lng)
        intent.putExtra("bol", bol)
        intent.putExtra("bte", bte)
        intent.putExtra("foat", foat)
        intent.putExtra("dbl", dbl)
        return intent
    }

    fun toBundle(): Bundle {
        val bundle = Bundle()
        bundle.putInt("id", id)
        bundle.putString("name", name)
        bundle.putShort("shrt", shrt)
        bundle.putLong("lng", lng)
        bundle.putBoolean("bol", bol)
        bundle.putByte("bte", bte)
        bundle.putFloat("foat", foat)
        bundle.putDouble("dbl", dbl)
        return bundle
    }

    companion object {
        @JvmStatic
        fun fromBundle(bundle: Bundle): BundleModelArgs {
            return BundleModelArgs(
                bundle.getInt("id", 0),
                bundle.getString("name", "yes"),
                bundle.getShort("shrt", 0),
                bundle.getLong("lng", 334),
                bundle.getBoolean("bol", false),
                bundle.getByte("bte", 0),
                bundle.getFloat("foat", 334.0F),
                bundle.getDouble("dbl", 0.0)
            )
        }

        @JvmStatic
        fun fromIntent(intent: Intent): BundleModelArgs {
            return BundleModelArgs(
                intent.getIntExtra("id", 0),
                intent.getStringExtra("name"),
                intent.getShortExtra("shrt", 0),
                intent.getLongExtra("lng", 334),
                intent.getBooleanExtra("bol", false),
                intent.getByteExtra("bte", 0),
                intent.getFloatExtra("foat", 334.0F),
                intent.getDoubleExtra("dbl", 0.0)
            )
        }

    }
}

FileSource builders

This is set of simple classes which helps generate Java/Kotlin code.

Usage

This is general case of using:

MethodSpec.Builder toIntent = MethodSpec.methodBuilder("toIntent")
    .addReturnType("Intent")
    .addStatement("val intent = Intent()");
toIntent.addStatement("return intent");

MethodSpec.Builder fromBundle = MethodSpec.methodBuilder("fromBundle")
    .addInheritanceModifier(MethodSpec.InheritanceModifier.STATIC)
    .addParam("bundle", "Bundle")
    .addReturnType(className)

ClassSpec classSpec = ClassSpec.classBuilder(className)
    .addConstructor(constructorSpecBuilder.build())
    .addAccessModifier(ClassSpec.AccessModifier.PUBLIC)
    .addInheritanceModifier(ClassSpec.InheritanceModifier.DATA)
    .addMethod(toIntent.build())
    .addMethod(fromBundle.build())
    .build();

FileSource.classFileBuilder(className)
    .addPackage(packageName)
    .addImport("android.content.Intent")
    .addImport("android.os.Bundle")
    .addClass(classSpec)
    .build()
    .writeTo(out, new KotlinWriter());

All static methods will be place inside companion object with @JvmStatic annotation.

This library is easier and simpler and less strictly then Java/Kotlin poets :)

ToDo list

  • Java and Kotlin code generation
  • Ignore for Args
  • Default values for Args (primitive types and String only)
  • Use ElementFieldParser and FileSource in Mappers classes
  • Support mapping org.jetbrains.annotations.Nullable value to org.jetbrains.annotations.NotNull value
  • Default values for Mapper

License

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.