Saltar a contenido

KAudit — variante Java (APT)

KSP solo procesa Kotlin. Para clases Java existe kaudit-apt, un annotation processor de javac (javax.annotation.processing) que reutiliza el mismo dominio (kaudit-core) y el mismo runtime (FieldChange), pero con un frontend distinto (kspkit-apt: javax.lang.modelkspkit-model) y un emisor JavaPoet (simétrico a KotlinPoet en el lado Kotlin, ambos aislados en kspkit-emit).

Es el pago concreto de la arquitectura hexagonal: se cambia el adaptador de entrada y el de salida; el núcleo de diff no se toca.

Instalación

// build.gradle.kts (proyecto Java)
dependencies {
    implementation("io.github.kuroxbyte:kaudit-annotations:0.2.0")
    implementation("io.github.kuroxbyte:kaudit-runtime:0.2.0")
    annotationProcessor("io.github.kuroxbyte:kaudit-apt:0.2.0")
}

El processor se auto-registra por META-INF/services; annotationProcessor(...) basta.

Qué genera

Por cada tipo Java @Auditable se genera una clase <Type>Auditor con un método estático:

public static List<FieldChange> diff(Account a, Account b) { ... }

(Java no tiene funciones de extensión; de ahí la estática en vez de la extensión Kotlin.) El List<FieldChange> de salida es exactamente el tipo del runtime compartido: renderízalo con tu propio código o persístelo como en la variante Kotlin.

Records y POJOs

Funciona con ambos:

// Record → accesor component()
@Auditable
public record Person(String email) {}

// POJO → getters getX()/isX()
@Auditable
public final class Product {
    private final String name; private final int price;
    public Product(String name, int price) { this.name = name; this.price = price; }
    public String getName() { return name; }
    public int getPrice() { return price; }
}

Anotaciones

Las anotaciones de propiedad de KAudit declaran @Target con FIELD/METHOD además de la posición Kotlin, así que se aplican directo sobre componentes de record, campos o getters:

@Auditable
public record Account(
    @AuditKey long id,          // identidad para diff de colecciones por clave
    String name,
    @Sensitive String apiKey,   // redactado: old/new en null
    @AuditIgnore long updatedAt,// fuera del diff
    Person owner                // @Auditable → recorre en cascada (owner.email)
) {}

Paridad completa con la variante Kotlin

La variante Java cubre todas las estrategias del dominio:

Caso Java (APT)
Escalar / @Sensitive / ignore ✅ igual que Kotlin
Anidado @Auditable ✅ recursión por delegación (owner.email)
Colección opaca (sin @AuditKey) ✅ un FieldChange con la lista entera
Colección por @AuditKey ✅ diff por identidad (members[42]), clave simple o compuesta
Map<K, V> ✅ diff por clave natural (settings[clave])
Tipos sealed ✅ vía instanceof pattern (mismo subtipo → recorre; distinto → objeto entero)

Normalización de tipos: el frontend APT traduce los nombres Java a los canónicos Kotlin (java.lang.Stringkotlin.String, intkotlin.Int, java.util.Listkotlin.collections.List…), de modo que las reglas de dominio (escritas contra FQNs Kotlin) aplican sin cambios a la entrada Java.

Un detalle: tipos anidados

El emisor deriva el paquete del generado del FQN del tipo. Declara los tipos @Auditable top-level (un record por archivo); los records anidados producirían un paquete inválido.

Verificación

kaudit-apt trae 5 tests end-to-end que corren javac con el processor y ejecutan el auditor generado (escalar/sensitive/anidado, Map, colección por @AuditKey, sealed, POJO). Ver también los ejemplos ejecutables.