Skip to content

Commit

Permalink
Merge branch 'feat-annotations'
Browse files Browse the repository at this point in the history
  • Loading branch information
YairLevi committed Jan 9, 2024
2 parents 5d5cf6f + 374e908 commit 0dc9772
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 38 deletions.
8 changes: 8 additions & 0 deletions src/main/kotlin/org/levi/coffee/annotations/BindAllMethods.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.levi.coffee.annotations

/**
* Use on a class instead of @BindMethod on all functions separately.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class BindAllMethods
10 changes: 0 additions & 10 deletions src/main/kotlin/org/levi/coffee/annotations/BindMethod.java

This file was deleted.

12 changes: 12 additions & 0 deletions src/main/kotlin/org/levi/coffee/annotations/BindMethod.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.levi.coffee.annotations

/**
* Specify to bind a certain function to the frontend.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
annotation class BindMethod
12 changes: 0 additions & 12 deletions src/main/kotlin/org/levi/coffee/annotations/BindType.java

This file was deleted.

22 changes: 22 additions & 0 deletions src/main/kotlin/org/levi/coffee/annotations/BindType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.levi.coffee.annotations

/**
* Specify a class to convert to a typescript type on the frontend.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class BindType(
/**
* Specifies which fields to pick for the typescript type.
* If empty of not provided at all, it will be ignored.
* If provided, will override the exclude tag.
* @return the exclusively included fields' names.
*/
val only: Array<String> = [],
/**
* Specifies which fields to ignore when building the typescript type.
* If empty or not provided, the type will include all fields.
* @return the ignored fields' names
*/
val ignore: Array<String> = [], // TODO: optionally, ignore some fields.
)
13 changes: 13 additions & 0 deletions src/main/kotlin/org/levi/coffee/annotations/IgnoreMethod.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.levi.coffee.annotations

/**
* When using @BindAllMethods on a class, use @IgnoreMethod to mark methods
* to be ignored and not bind to the frontend.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
annotation class IgnoreMethod
27 changes: 27 additions & 0 deletions src/main/kotlin/org/levi/coffee/internal/BindFilter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.levi.coffee.internal

import org.levi.coffee.annotations.BindAllMethods
import org.levi.coffee.annotations.BindMethod
import org.levi.coffee.annotations.BindType
import org.levi.coffee.annotations.IgnoreMethod
import java.lang.reflect.Field
import java.lang.reflect.Method

object BindFilter {
fun methodsOf(c: Class<*>): List<Method> {
if (c.isAnnotationPresent(BindAllMethods::class.java)) {
return c.declaredMethods.filter { !it.isAnnotationPresent(IgnoreMethod::class.java) }
}
return c.declaredMethods.filter { it.isAnnotationPresent(BindMethod::class.java) }
}

fun fieldsOf(c: Class<*>): List<Field> {
// Assuming "@BindType()" present on class "c"

val annotation = c.getAnnotation(BindType::class.java)
if (annotation.only.isNotEmpty()) {
return c.declaredFields.filter { annotation.only.contains(it.name) }
}
return c.declaredFields.filter { !annotation.ignore.contains(it.name) }
}
}
20 changes: 10 additions & 10 deletions src/main/kotlin/org/levi/coffee/internal/CodeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,20 @@ internal object CodeGenerator {
fun generateTypes(vararg objects: Any) {
try {
val writer = PrintWriter(TYPES_FILE_PATH)
val classes = objects.map { it.javaClass }
val classes = objects.map { it::class.java }
TypeConverter.boundTypes.addAll(classes.map { it.simpleName })
for (c in classes) {
if (!c.isAnnotationPresent(BindType::class.java)) {
continue
}

val fieldsToBind = BindFilter.fieldsOf(c)

// Declare type and export
writer.println("export type ${c.simpleName} = {")

// Add fields and map the types from java to typescript
for (field in c.declaredFields) {
for (field in fieldsToBind) {
val name = field.name
val type = TypeConverter.convert(field.genericType, false)
writer.println("\t$name: $type")
Expand All @@ -66,7 +68,7 @@ internal object CodeGenerator {

fun generateFunctions(vararg objects: Any) {
for (c in objects.map { it.javaClass }) {
val methodCount = c.declaredMethods.filter { it.isAnnotationPresent(BindMethod::class.java) }.size
val methodCount = BindFilter.methodsOf(c).size
if (methodCount == 0) {
continue
}
Expand All @@ -82,9 +84,9 @@ internal object CodeGenerator {
FileUtil.createOrReplaceFile(path)
val writer = PrintWriter(path)

for (method in c.declaredMethods) {
if (!method.isAnnotationPresent(BindMethod::class.java)) continue
val methodsToBind = BindFilter.methodsOf(c)

for (method in methodsToBind) {
val methodName = method.name
val argsString = method.parameters.joinToString(",") { it.name }
writer.println("export function $methodName($argsString) {")
Expand All @@ -105,13 +107,11 @@ internal object CodeGenerator {
FileUtil.createOrReplaceFile(path)
val writer = PrintWriter(path)

writer.println("import * as t from '../types';\n")
val methodsToBind = BindFilter.methodsOf(c)

for (method in c.declaredMethods.sortedBy { it.name }) {
if (!method.isAnnotationPresent(BindMethod::class.java)) {
continue
}
writer.println("import * as t from '../types';\n")

for (method in methodsToBind) {
val argsString = method.parameters.joinToString(", ") {
"${it.name}: ${TypeConverter.convert(it.parameterizedType, true)}"
}
Expand Down
15 changes: 10 additions & 5 deletions src/main/kotlin/org/levi/coffee/internal/TypeConverter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.levi.coffee.internal

import org.slf4j.LoggerFactory
import java.lang.reflect.Type
import java.util.*
import java.util.regex.Pattern

internal object TypeConverter {
Expand Down Expand Up @@ -57,12 +56,18 @@ internal object TypeConverter {
val pattern = Pattern.compile("[a-zA-Z0-9]+")
val matcher = pattern.matcher(type)

// Each type found, swap for the corresponding type in Typescript
val types = HashSet<String>()
while (matcher.find()) {
val javaType = matcher.group()
types.add(matcher.group())
}

// Each type found, swap for the corresponding type in Typescript
for (javaType in types) {
if (!jsTypes.containsKey(javaType) && !boundTypes.contains(javaType)) {
log.error("java type $javaType is not recognized. Did you forget to @BindType ?\n" +
"Used 'any' instead, just in case.")
log.error(
"java type $javaType is not recognized. Did you forget to @BindType ?\n" +
"Used 'any' instead, just in case."
)
type = type.replace(javaType, jsTypes.getOrDefault(javaType, "any"))
} else if (!jsTypes.containsKey(javaType) && addTypePrefix) {
type = type.replace(javaType, "t.$javaType")
Expand Down
6 changes: 5 additions & 1 deletion src/test/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import com.google.gson.Gson
import org.levi.coffee.Ipc
import org.levi.coffee.Window
import org.levi.coffee.annotations.BindAllMethods
import org.levi.coffee.annotations.BindMethod
import org.levi.coffee.annotations.BindType
import org.levi.coffee.annotations.IgnoreMethod
import java.io.BufferedReader
import java.io.File

@BindType(ignore = ["age"])
class Person(
val name: String = "",
var age: Int = 0,
val hobbies: List<String> = emptyList(),
val string: Map<Person, List<Person>> = emptyMap(),
) {

@BindMethod
fun addTwoNumbers(a: Int, b: Int): Int {
return a + b;
}
Expand Down

0 comments on commit 0dc9772

Please sign in to comment.