├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── themes.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── io
│ │ │ │ └── johnsonlee
│ │ │ │ └── template
│ │ │ │ └── app
│ │ │ │ ├── Sample.kt
│ │ │ │ ├── Provider.kt
│ │ │ │ ├── SampleImpl.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── io
│ │ │ └── johnsonlee
│ │ │ └── example
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── io
│ │ └── johnsonlee
│ │ └── example
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── example
├── src
│ └── main
│ │ ├── resources
│ │ ├── META-INF
│ │ │ └── gradle
│ │ │ │ └── incremental.annotation.processors
│ │ └── template
│ │ │ ├── AutoFactory.kt.mustache
│ │ │ └── AutoFactory.java.mustache
│ │ └── kotlin
│ │ └── io
│ │ └── johnsonlee
│ │ └── codegen
│ │ └── example
│ │ ├── Constants.kt
│ │ ├── Factory.kt
│ │ ├── ObjectPool.kt
│ │ ├── AutoFactory.kt
│ │ └── AutoFactoryCodegen.kt
└── build.gradle.kts
├── settings.gradle.kts
├── .gitattributes
├── compiler
├── src
│ └── main
│ │ └── kotlin
│ │ └── io
│ │ └── johnsonlee
│ │ └── codegen
│ │ ├── model
│ │ └── Model.kt
│ │ ├── compiler
│ │ ├── DeclaredTypeExtension.kt
│ │ ├── VariableElementExtension.kt
│ │ ├── Output.kt
│ │ ├── ElementExtension.kt
│ │ ├── AnnotationMirrorExtension.kt
│ │ ├── ProcessingEnvironmentExtension.kt
│ │ └── CodegenProcessor.kt
│ │ └── template
│ │ └── TemplateEngine.kt
└── build.gradle.kts
├── .gitignore
├── mustache
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── io
│ └── johnsonlee
│ └── codegen
│ └── template
│ └── mustache
│ └── MustacheEngine.kt
├── .github
└── workflows
│ ├── build.yml
│ └── publish.yml
├── velocity
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── io
│ └── johnsonlee
│ └── codegen
│ └── template
│ └── velocity
│ └── VelocityEngine.kt
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.useAndroidX=true
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
--------------------------------------------------------------------------------
/app/src/main/java/io/johnsonlee/template/app/Sample.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.template.app
2 |
3 | interface Sample {
4 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/src/main/resources/META-INF/gradle/incremental.annotation.processors:
--------------------------------------------------------------------------------
1 | io.johnsonlee.codegen.example.AutoFactoryCodegen,isolating
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/java/io/johnsonlee/template/app/Provider.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.template.app
2 |
3 | interface Provider {
4 |
5 | fun get(): T
6 |
7 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnsonlee/codegen/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "codegen"
2 |
3 | include(":app")
4 | include(":compiler")
5 | include(":example")
6 | include(":mustache")
7 | include(":velocity")
8 |
--------------------------------------------------------------------------------
/example/src/main/kotlin/io/johnsonlee/codegen/example/Constants.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.example
2 |
3 | internal const val PKG_GENERATED = "io.johnsonlee.codegen.generated"
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # These are explicitly windows files and should use crlf
5 | *.bat text eol=crlf
6 |
7 |
--------------------------------------------------------------------------------
/example/src/main/kotlin/io/johnsonlee/codegen/example/Factory.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.example
2 |
3 | interface Factory {
4 |
5 | val type: Class
6 |
7 | fun newInstance(pool: ObjectPool): T
8 |
9 | }
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/model/Model.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.model
2 |
3 | interface Model {
4 | val qualifiedName: String
5 | val simpleName: String
6 | val packageName: String
7 | val references: Set
8 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/example/src/main/kotlin/io/johnsonlee/codegen/example/ObjectPool.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.example
2 |
3 | object ObjectPool {
4 |
5 | @JvmStatic
6 | fun get(type: Class): T = TODO()
7 |
8 | inline fun get(): T = get(T::class.java)
9 |
10 | }
--------------------------------------------------------------------------------
/example/src/main/kotlin/io/johnsonlee/codegen/example/AutoFactory.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.example
2 |
3 | import kotlin.reflect.KClass
4 |
5 | @Retention(AnnotationRetention.BINARY)
6 | @Target(AnnotationTarget.CLASS)
7 | annotation class AutoFactory(
8 | val value: KClass<*>
9 | )
10 |
--------------------------------------------------------------------------------
/app/src/main/java/io/johnsonlee/template/app/SampleImpl.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.template.app
2 |
3 | import android.content.Context
4 | import io.johnsonlee.codegen.example.AutoFactory
5 |
6 | @AutoFactory(Sample::class)
7 | class SampleImpl(
8 | val context: Provider
9 | ) : Sample {
10 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/DeclaredTypeExtension.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | import com.google.auto.common.MoreTypes
4 | import javax.lang.model.element.TypeElement
5 | import javax.lang.model.type.DeclaredType
6 |
7 | fun DeclaredType.asTypeElement(): TypeElement = MoreTypes.asTypeElement(this)
8 |
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/VariableElementExtension.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | import com.google.auto.common.MoreTypes
4 | import javax.lang.model.element.Element
5 | import javax.lang.model.element.VariableElement
6 |
7 | fun VariableElement.asElement(): Element = MoreTypes.asElement(this.asType())
8 |
9 |
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/template/TemplateEngine.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.template
2 |
3 | import io.johnsonlee.codegen.model.Model
4 | import java.io.Writer
5 |
6 | interface TemplateEngine {
7 |
8 | val name: String
9 |
10 | val extension: String
11 |
12 | fun render(template: String, model: Model, writer: Writer): Writer
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/io/johnsonlee/template/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.template.app
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 |
6 | class MainActivity : AppCompatActivity() {
7 | override fun onCreate(savedInstanceState: Bundle?) {
8 | super.onCreate(savedInstanceState)
9 | setContentView(R.layout.activity_main)
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/test/java/io/johnsonlee/example/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.example
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/Output.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | interface Output {
4 | val extension: String
5 | }
6 |
7 | sealed class Source(override val extension: String) : Output {
8 | object Java: Source("java")
9 | object Kotlin: Source("kt")
10 | object KotlinScript: Source("kts")
11 | object Groovy: Source("groovy")
12 | }
13 |
14 | interface Resource : Output {
15 | val prefix: String
16 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | .gradle
4 | /build/
5 |
6 | # Ignore Gradle GUI config
7 | gradle-app.setting
8 |
9 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
10 | !gradle-wrapper.jar
11 |
12 | # Cache of project
13 | .gradletasknamecache
14 |
15 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
16 | # gradle/wrapper/gradle-wrapper.properties
17 |
18 | # Ignore Gradle build output directory
19 | build
20 |
21 | local.properties
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/example/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | kotlin("kapt")
4 | }
5 |
6 | repositories {
7 | mavenCentral()
8 | google()
9 | }
10 |
11 | dependencies {
12 | kapt("com.google.auto.service:auto-service:1.0")
13 | implementation("com.google.auto.service:auto-service:1.0")
14 | implementation(kotlin("bom"))
15 | implementation(kotlin("stdlib"))
16 | implementation(kotlin("reflect"))
17 | implementation(project(":compiler"))
18 | implementation(project(":mustache"))
19 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/mustache/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | kotlin("kapt")
4 | id("io.johnsonlee.sonatype-publish-plugin")
5 | }
6 |
7 | repositories {
8 | mavenCentral()
9 | google()
10 | }
11 |
12 | java {
13 | sourceCompatibility = JavaVersion.VERSION_1_8
14 | targetCompatibility = JavaVersion.VERSION_1_8
15 | }
16 |
17 | dependencies {
18 | implementation(kotlin("bom"))
19 | implementation(kotlin("stdlib"))
20 | implementation(project(":compiler"))
21 | implementation("com.github.spullara.mustache.java:compiler:0.9.10")
22 | }
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Run unit tests
2 |
3 | on:
4 | push:
5 | branches: [ '*' ]
6 | pull_request:
7 | branches: [ '*' ]
8 |
9 | repository_dispatch:
10 | types: [test]
11 |
12 | jobs:
13 | run-unit-test:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v2
19 |
20 | - name: Setup Java
21 | uses: actions/setup-java@v2
22 | with:
23 | distribution: 'adopt'
24 | java-version: '8'
25 |
26 | - name: build
27 | run: ./gradlew check -S --no-daemon
28 |
--------------------------------------------------------------------------------
/example/src/main/resources/template/AutoFactory.kt.mustache:
--------------------------------------------------------------------------------
1 | /*
2 | * AUTO-GENERATED, DO NOT MODIFY THIS FILE!
3 | */
4 | package io.johnsonlee.codegen.generated
5 |
6 | import io.johnsonlee.codegen.example.Factory
7 | import io.johnsonlee.codegen.example.ObjectPool
8 |
9 | class {{simpleName}} : Factory<{{implementation}}> {
10 |
11 | override val type = {{implementation}}::class.java
12 |
13 | override fun newInstance(pool: ObjectPool) = {{implementation}}(
14 | {{#args}}
15 | pool.get<{{type}}>() {{^isLast}},{{/isLast}}
16 | {{/args}}
17 | );
18 |
19 | }
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/ElementExtension.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | import com.google.auto.common.MoreElements
4 | import javax.lang.model.element.AnnotationMirror
5 | import javax.lang.model.element.Element
6 | import javax.lang.model.element.TypeElement
7 |
8 | fun Element.asTypeElement(): TypeElement = MoreElements.asType(this)
9 |
10 | inline fun Element.getAnnotationMirror(): AnnotationMirror? {
11 | return MoreElements.getAnnotationMirror(this, T::class.java).takeIf {
12 | it.isPresent
13 | }?.get()
14 | }
15 |
--------------------------------------------------------------------------------
/compiler/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | kotlin("kapt")
4 | id("io.johnsonlee.sonatype-publish-plugin")
5 | }
6 |
7 | repositories {
8 | mavenCentral()
9 | google()
10 | }
11 |
12 | java {
13 | sourceCompatibility = JavaVersion.VERSION_1_8
14 | targetCompatibility = JavaVersion.VERSION_1_8
15 | }
16 |
17 | dependencies {
18 | kapt("com.google.auto.service:auto-service:1.0")
19 | implementation("com.google.auto.service:auto-service:1.0")
20 | implementation(kotlin("bom"))
21 | implementation(kotlin("stdlib"))
22 | implementation(kotlin("reflect"))
23 | }
--------------------------------------------------------------------------------
/velocity/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | kotlin("kapt")
4 | id("io.johnsonlee.sonatype-publish-plugin")
5 | }
6 |
7 | repositories {
8 | mavenCentral()
9 | google()
10 | }
11 |
12 | java {
13 | sourceCompatibility = JavaVersion.VERSION_1_8
14 | targetCompatibility = JavaVersion.VERSION_1_8
15 | }
16 |
17 | dependencies {
18 | implementation(kotlin("bom"))
19 | implementation(kotlin("stdlib"))
20 | implementation(kotlin("reflect"))
21 | implementation(project(":compiler"))
22 | implementation("org.apache.velocity.tools:velocity-tools-generic:3.1")
23 | }
--------------------------------------------------------------------------------
/example/src/main/resources/template/AutoFactory.java.mustache:
--------------------------------------------------------------------------------
1 | /*
2 | * AUTO-GENERATED, DO NOT MODIFY THIS FILE!
3 | */
4 | package io.johnsonlee.codegen.generated;
5 |
6 | import io.johnsonlee.codegen.example.Factory;
7 | import io.johnsonlee.codegen.example.ObjectPool;
8 |
9 | class {{simpleName}} implements Factory<{{implementation}}> {
10 |
11 | @Override
12 | public Class<{{implementation}}> getType() {
13 | return {{implementation}}.class;
14 | }
15 |
16 | @Override
17 | public {{implementation}} newInstance(final ObjectPool pool) {
18 | return new {{implementation}}(
19 | {{#args}}
20 | pool.get({{typeErasure}}.class) {{^isLast}},{{/isLast}}
21 | {{/args}}
22 | );
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/io/johnsonlee/example/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.example
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 | import org.junit.Test
6 | import org.junit.runner.RunWith
7 |
8 | import org.junit.Assert.*
9 |
10 | /**
11 | * Instrumented test, which will execute on an Android device.
12 | *
13 | * See [testing documentation](http://d.android.com/tools/testing).
14 | */
15 | @RunWith(AndroidJUnit4::class)
16 | class ExampleInstrumentedTest {
17 | @Test
18 | fun useAppContext() {
19 | // Context of the app under test.
20 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
21 | assertEquals("io.johnsonlee.example", appContext.packageName)
22 | }
23 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/mustache/src/main/kotlin/io/johnsonlee/codegen/template/mustache/MustacheEngine.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.template.mustache
2 |
3 | import com.github.mustachejava.DefaultMustacheFactory
4 | import com.github.mustachejava.MustacheFactory
5 | import io.johnsonlee.codegen.model.Model
6 | import io.johnsonlee.codegen.template.TemplateEngine
7 | import java.io.Writer
8 |
9 | class MustacheEngine : TemplateEngine {
10 |
11 | private val mustacheFactory: MustacheFactory by lazy {
12 | object : DefaultMustacheFactory() {
13 | override fun encode(value: String, writer: Writer) {
14 | writer.write(value)
15 | }
16 | }
17 | }
18 |
19 | override val name: String = "mustache"
20 |
21 | override val extension: String = name
22 |
23 | override fun render(template: String, model: Model, writer: Writer): Writer {
24 | return mustacheFactory.compile(template).execute(writer, model)
25 | }
26 |
27 |
28 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codegen
2 |
3 | A lightweight framework for code generating.
4 |
5 | ## Why Codegen?
6 |
7 | In Java/Kotlin world, engineers usually use [JavaPoet](https://github.com/square/javapoet) or [KotlinPoet](https://github.com/square/kotlinpoet) for code generating, those tool libraries are really cool, but it increases code maintainability, using [JavaPoet](https://github.com/square/javapoet) or [KotlinPoet](https://github.com/square/kotlinpoet) to generate Java/Kotlin source code like using JSP languages to write a web page, you never know what you have written after a few months.
8 |
9 | Codegen separates the generating logic into template and data model, it a little bit like SSR (Server Side Rendering), and provides the well-known template engines integration, e.g. [Mustache](https://mustache.github.io/), [Velocity](https://velocity.apache.org/), it also supports custom template engine.
10 |
11 | ## Example
12 |
13 | See the example of [AutoFactoryCodegen.kt](./example/src/main/kotlin/io/johnsonlee/codegen/example/AutoFactoryCodegen.kt)
14 |
15 |
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/AnnotationMirrorExtension.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | import com.google.auto.common.AnnotationMirrors
4 | import com.google.auto.common.MoreTypes
5 | import javax.lang.model.element.AnnotationMirror
6 | import javax.lang.model.element.AnnotationValue
7 | import javax.lang.model.type.DeclaredType
8 | import javax.lang.model.type.TypeMirror
9 | import javax.lang.model.util.SimpleAnnotationValueVisitor8
10 |
11 | private object AnnotationValueVisitor : SimpleAnnotationValueVisitor8, Void>() {
12 | override fun visitType(typeMirror: TypeMirror?, v: Void?): Set {
13 | return setOf(MoreTypes.asDeclared(typeMirror))
14 | }
15 |
16 | override fun visitArray(values: MutableList, v: Void?): Set {
17 | return values.map {
18 | it.accept(this, null)
19 | }.flatten().toSet()
20 | }
21 | }
22 |
23 | val AnnotationMirror.value: Set
24 | get() = AnnotationMirrors.getAnnotationValue(this, "value").accept(AnnotationValueVisitor, null)
--------------------------------------------------------------------------------
/velocity/src/main/kotlin/io/johnsonlee/codegen/template/velocity/VelocityEngine.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.template.velocity
2 |
3 | import io.johnsonlee.codegen.model.Model
4 | import io.johnsonlee.codegen.template.TemplateEngine
5 | import org.apache.velocity.VelocityContext
6 | import org.apache.velocity.app.Velocity
7 | import java.io.Writer
8 | import kotlin.reflect.KParameter
9 | import kotlin.reflect.KProperty0
10 | import kotlin.reflect.KVisibility
11 |
12 | class VelocityEngine : TemplateEngine {
13 |
14 | override val name: String = "velocity"
15 |
16 | override val extension: String = "vm"
17 |
18 | init {
19 | Velocity.init()
20 | }
21 |
22 | override fun render(template: String, model: Model, writer: Writer): Writer {
23 | val context = VelocityContext(model.toMap())
24 | Velocity.mergeTemplate(template, "UTF-8", context, writer)
25 | return writer
26 | }
27 |
28 | }
29 |
30 | private fun Model.toMap(): Map = javaClass.kotlin.members.filter {
31 | it.visibility == KVisibility.PUBLIC
32 | }.mapNotNull { member ->
33 | when (member) {
34 | is KProperty0 -> member.name to member.get()
35 | is Function<*> -> member.parameters.takeIf(List::isEmpty)?.let {
36 | member.name to member.call()
37 | }
38 | else -> null
39 | }
40 | }.toMap()
--------------------------------------------------------------------------------
/compiler/src/main/kotlin/io/johnsonlee/codegen/compiler/ProcessingEnvironmentExtension.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.compiler
2 |
3 | import java.io.PrintWriter
4 | import java.io.StringWriter
5 | import javax.annotation.processing.ProcessingEnvironment
6 | import javax.lang.model.element.AnnotationMirror
7 | import javax.lang.model.element.Element
8 | import javax.tools.Diagnostic
9 |
10 | fun ProcessingEnvironment.debug(msg: String) {
11 | if (options.containsKey("debug")) {
12 | messager.printMessage(Diagnostic.Kind.NOTE, msg)
13 | }
14 | }
15 |
16 | fun ProcessingEnvironment.debug(msg: String, element: Element, annotation: AnnotationMirror) {
17 | if (options.containsKey("debug")) {
18 | messager.printMessage(Diagnostic.Kind.NOTE, msg, element, annotation)
19 | }
20 | }
21 |
22 | fun ProcessingEnvironment.warn(msg: String, element: Element, annotation: AnnotationMirror) {
23 | messager.printMessage(Diagnostic.Kind.WARNING, msg, element, annotation)
24 | }
25 |
26 | fun ProcessingEnvironment.error(msg: String, element: Element, annotation: AnnotationMirror) {
27 | messager.printMessage(Diagnostic.Kind.ERROR, msg, element, annotation)
28 | }
29 |
30 | fun ProcessingEnvironment.fatal(e: Throwable) {
31 | val stack = StringWriter().run {
32 | e.printStackTrace(PrintWriter(this))
33 | toString()
34 | }
35 | messager.printMessage(Diagnostic.Kind.ERROR, "FATAL ERROR: $stack")
36 | }
37 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish build to Sonatype
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 | repository_dispatch:
8 | types: [publish]
9 |
10 | jobs:
11 | publish:
12 | runs-on: ubuntu-latest
13 | if: github.repository == 'johnsonlee/codegen'
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v2
18 |
19 | - name: Setup Java
20 | uses: actions/setup-java@v2
21 | with:
22 | distribution: 'adopt'
23 | java-version: '11'
24 |
25 | - name: publish to sonatype
26 | run: |
27 | echo "Release version ${GITHUB_REF/refs\/tags\/v/} ..."
28 | echo "Create GPG private key"
29 | echo $GPG_KEY_ARMOR | base64 --decode > ${GITHUB_WORKSPACE}/secring.gpg
30 | ./gradlew clean initializeSonatypeStagingRepository publishToSonatype closeAndReleaseRepository -S --no-daemon \
31 | -Pversion=${GITHUB_REF/refs\/tags\/v/} \
32 | -POSSRH_USERNAME=${OSSRH_USERNAME} \
33 | -POSSRH_PASSWORD=${OSSRH_PASSWORD} \
34 | -POSSRH_PACKAGE_GROUP=${OSSRH_PACKAGE_GROUP} \
35 | -Psigning.keyId=${GPG_KEY_ID} \
36 | -Psigning.password=${GPG_PASSPHRASE} \
37 | -Psigning.secretKeyRingFile=${GITHUB_WORKSPACE}/secring.gpg
38 | env:
39 | JAVA_OPTS: -Xmx8g -XX:MaxPermSize=1g -XX:MetaspaceSize=1g -Dfile.encoding=UTF-8
40 | JVM_OPTS: -Xmx8g -XX:MaxPermSize=1g -XX:MetaspaceSize=1g -Dfile.encoding=UTF-8
41 | GPG_KEY_ARMOR: ${{ secrets.GPG_KEY_ARMOR }}
42 | GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
43 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
44 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
45 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
46 | OSSRH_PACKAGE_GROUP: ${{ secrets.OSSRH_PACKAGE_GROUP }}
47 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | id("kotlin-kapt")
5 | }
6 |
7 | repositories {
8 | mavenCentral()
9 | google()
10 | }
11 |
12 | android {
13 | compileSdkVersion(30)
14 |
15 | defaultConfig {
16 | applicationId = "io.johnsonlee.codegen.example"
17 | minSdkVersion(21)
18 | targetSdkVersion(30)
19 | versionCode = 1
20 | versionName = "${project.version}"
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | getByName("debug") {
27 | isMinifyEnabled = false
28 | }
29 | getByName("release") {
30 | isMinifyEnabled = false
31 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
32 | }
33 | }
34 | compileOptions {
35 | sourceCompatibility = JavaVersion.VERSION_1_8
36 | targetCompatibility = JavaVersion.VERSION_1_8
37 | }
38 | kotlinOptions {
39 | jvmTarget = "1.8"
40 | }
41 | }
42 |
43 | dependencies {
44 | kapt(project(":example"))
45 | implementation(project(":example"))
46 | implementation(kotlin("bom"))
47 | implementation(kotlin("stdlib"))
48 | implementation("androidx.core:core-ktx:1.6.0")
49 | implementation("androidx.appcompat:appcompat:1.3.1")
50 | implementation("androidx.activity:activity-ktx:1.3.1")
51 | implementation("androidx.fragment:fragment-ktx:1.3.6")
52 | implementation("androidx.lifecycle:lifecycle-common:2.3.1")
53 | implementation("androidx.constraintlayout:constraintlayout:2.1.1")
54 | testImplementation("junit:junit:4.+")
55 | androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
56 | androidTestImplementation("androidx.test:runner:1.4.0")
57 | androidTestImplementation("androidx.test:rules:1.4.0")
58 | }
59 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/example/src/main/kotlin/io/johnsonlee/codegen/example/AutoFactoryCodegen.kt:
--------------------------------------------------------------------------------
1 | package io.johnsonlee.codegen.example
2 |
3 | import com.google.auto.service.AutoService
4 | import io.johnsonlee.codegen.compiler.CodegenProcessor
5 | import io.johnsonlee.codegen.compiler.Source
6 | import io.johnsonlee.codegen.compiler.asElement
7 | import io.johnsonlee.codegen.compiler.asTypeElement
8 | import io.johnsonlee.codegen.compiler.error
9 | import io.johnsonlee.codegen.compiler.getAnnotationMirror
10 | import io.johnsonlee.codegen.compiler.value
11 | import io.johnsonlee.codegen.model.Model
12 | import io.johnsonlee.codegen.template.TemplateEngine
13 | import io.johnsonlee.codegen.template.mustache.MustacheEngine
14 | import javax.annotation.processing.ProcessingEnvironment
15 | import javax.annotation.processing.Processor
16 | import javax.annotation.processing.RoundEnvironment
17 | import javax.annotation.processing.SupportedSourceVersion
18 | import javax.lang.model.SourceVersion
19 | import javax.lang.model.element.AnnotationMirror
20 | import javax.lang.model.element.ElementKind
21 | import javax.lang.model.element.ExecutableElement
22 | import javax.lang.model.element.TypeElement
23 | import javax.lang.model.type.DeclaredType
24 |
25 | @AutoService(Processor::class)
26 | @SupportedSourceVersion(SourceVersion.RELEASE_8)
27 | class AutoFactoryCodegen : CodegenProcessor() {
28 |
29 | private val mustache: TemplateEngine by lazy(::MustacheEngine)
30 |
31 | override val engine: TemplateEngine = mustache
32 |
33 | override fun onProcessing(
34 | processingEnv: ProcessingEnvironment,
35 | annotations: MutableSet,
36 | roundEnv: RoundEnvironment
37 | ) {
38 | roundEnv.onEachAnnotatedElement { element ->
39 | val implementation = element.asTypeElement()
40 | val mirror = element.getAnnotationMirror() ?: return@onEachAnnotatedElement
41 | mirror.value.takeIf(Set::isNotEmpty)?.map(DeclaredType::asTypeElement)?.onEach { api ->
42 | if (checkImplementation(implementation, api)) {
43 | generateFactory(implementation, mirror)
44 | } else {
45 | processingEnv.error(
46 | "${implementation.qualifiedName} does not implement ${api.qualifiedName}",
47 | element,
48 | mirror
49 | )
50 | }
51 | } ?: processingEnv.error("No interface provided for element!", element, mirror)
52 | }
53 | }
54 |
55 | private fun checkImplementation(implementation: TypeElement, api: TypeElement): Boolean {
56 | val verify: String? by processingEnv.options.withDefault { null }
57 | if (verify == null || !java.lang.Boolean.parseBoolean(verify)) {
58 | return true
59 | }
60 | return implementation.isSubtypeOf(api)
61 | }
62 |
63 | private fun generateFactory(implementation: TypeElement, mirror: AnnotationMirror) {
64 | val constructors = implementation.enclosedElements.filter {
65 | it.kind == ElementKind.CONSTRUCTOR
66 | }.takeIf {
67 | it.size <= 1
68 | }?.map {
69 | it as ExecutableElement
70 | } ?: return processingEnv.error(
71 | "Too many constructors: ${implementation.qualifiedName}",
72 | implementation,
73 | mirror
74 | )
75 | val params = constructors.singleOrNull()?.parameters
76 | val argc = params?.count() ?: 0
77 | val args = params?.mapIndexed { i, it ->
78 | mapOf(
79 | "type" to it.asType(),
80 | "typeErasure" to processingEnv.typeUtils.erasure(it.asType()),
81 | "isInterface" to (it.asElement().kind == ElementKind.INTERFACE),
82 | "isLast" to (i + 1 == argc)
83 | )
84 | } ?: emptyList()
85 | generate(
86 | "template/AutoFactory",
87 | AutoFactoryModel(implementation.qualifiedName.toString(), args),
88 | Source.Kotlin
89 | )
90 | }
91 | }
92 |
93 | internal data class AutoFactoryModel(
94 | val implementation: String,
95 | val args: List