tech.ydb.yoj.databind.schema.configuration.SchemaRegistry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of yoj-databind Show documentation
Show all versions of yoj-databind Show documentation
Core data-binding logic used by YOJ (YDB ORM for Java) to convert
between Java objects and database rows (or anything representable by
a Java Map, really).
The newest version!
package tech.ydb.yoj.databind.schema.configuration;
import com.google.common.annotations.VisibleForTesting;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.With;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.databind.schema.naming.AnnotationFirstNamingStrategy;
import tech.ydb.yoj.databind.schema.naming.NamingStrategy;
import tech.ydb.yoj.databind.schema.reflect.Reflector;
import tech.ydb.yoj.databind.schema.reflect.StdReflector;
import javax.annotation.Nullable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static lombok.AccessLevel.PRIVATE;
@RequiredArgsConstructor
public final class SchemaRegistry {
private static final SchemaRegistry DEFAULT = new SchemaRegistry(StdReflector.instance);
private static final NamingStrategy DEFAULT_NAMING_STRATEGY = AnnotationFirstNamingStrategy.instance;
private final ConcurrentMap, Schemas> schemasByType = new ConcurrentHashMap<>();
private final NamingOverrides namingOverrides = new NamingOverrides();
private final Reflector reflector;
@NonNull
public static SchemaRegistry getDefault() {
return DEFAULT;
}
@NonNull
@SuppressWarnings("rawtypes")
public > S getOrCreate(@NonNull Class extends Schema> schemaClass,
@NonNull SchemaCreator ctor,
@NonNull SchemaKey schemaKey) {
return schemasByType
.computeIfAbsent(schemaKey, __ -> new Schemas(reflector))
.getOrCreate(schemaClass, ctor, schemaKeyWithProperNaming(schemaKey));
}
private SchemaKey schemaKeyWithProperNaming(SchemaKey sk) {
return sk.withNamingStrategy(namingOverrides.getOrDefault(sk.clazz, sk.namingStrategy));
}
@NonNull
public NamingOverrides namingOverrides() {
return namingOverrides;
}
/**
* Only for testing. Do not use in production code.
*/
@VisibleForTesting
public void clear() {
schemasByType.clear();
namingOverrides.clear();
}
public record SchemaKey(@NonNull Class clazz, @NonNull @With NamingStrategy namingStrategy) {
public static SchemaKey of(@NonNull Class clazz) {
return of(clazz, null);
}
public static SchemaKey of(@NonNull Class clazz, @Nullable NamingStrategy namingStrategy) {
return new SchemaKey<>(clazz, namingStrategy == null ? DEFAULT_NAMING_STRATEGY : namingStrategy);
}
public SchemaKey withClazz(@NonNull Class clazz) {
return new SchemaKey<>(clazz, this.namingStrategy);
}
}
@FunctionalInterface
public interface SchemaCreator> {
S create(SchemaKey key, Reflector reflector);
}
@RequiredArgsConstructor
private static final class Schemas {
@SuppressWarnings("rawtypes")
private final ConcurrentMap, Schema>> schemas = new ConcurrentHashMap<>();
private final Reflector reflector;
@SuppressWarnings({"unchecked", "rawtypes"})
public > S getOrCreate(Class extends Schema> schemaClass,
SchemaCreator ctor,
SchemaKey schemaKey) {
return (S) schemas.computeIfAbsent(schemaClass, __ -> ctor.create(schemaKey, reflector));
}
}
@RequiredArgsConstructor(access = PRIVATE)
public static final class NamingOverrides {
private final ConcurrentMap, NamingStrategy> overrides = new ConcurrentHashMap<>();
public void add(@NonNull Class> type, @NonNull NamingStrategy namingStrategy) {
if (overrides.putIfAbsent(type, namingStrategy) != null) {
throw new IllegalArgumentException("Naming strategy for '" + type.getName() + "' already has an override");
}
}
public boolean contains(@NonNull Class> type) {
return overrides.containsKey(type);
}
private NamingStrategy getOrDefault(@NonNull Class> type, @NonNull NamingStrategy defaultStrategy) {
return overrides.getOrDefault(type, defaultStrategy);
}
@VisibleForTesting
public void clear() {
overrides.clear();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy