ExtensionRegistry.java
package com.taxonomy.shared.extension.runtime;
import com.taxonomy.shared.extension.ExtensionDescriptor;
import com.taxonomy.shared.extension.ExtensionKind;
import com.taxonomy.shared.extension.TaxonomyExtension;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
/** Spring runtime registry for all internal taxonomy extension beans. */
@Service
public class ExtensionRegistry {
private final Map<ExtensionKind, Map<String, TaxonomyExtension>> byKind;
public ExtensionRegistry(List<TaxonomyExtension> extensions) {
EnumMap<ExtensionKind, Map<String, TaxonomyExtension>> registrations =
new EnumMap<>(ExtensionKind.class);
for (ExtensionKind kind : ExtensionKind.values()) {
registrations.put(kind, new LinkedHashMap<>());
}
extensions.stream()
.map(this::validatedRegistration)
.sorted(Comparator.comparing(Registration::kind)
.thenComparing(Registration::normalizedId))
.forEach(registration -> {
Map<String, TaxonomyExtension> extensionsById =
registrations.get(registration.kind());
TaxonomyExtension previous = extensionsById.putIfAbsent(
registration.normalizedId(), registration.extension());
if (previous != null) {
throw new IllegalStateException(
"Duplicate extension ID for kind %s: normalized ID '%s' conflicts between %s (id='%s') and %s (id='%s')"
.formatted(
registration.kind(),
registration.normalizedId(),
previous.getClass().getName(),
previous.id(),
registration.extension().getClass().getName(),
registration.extension().id()));
}
});
EnumMap<ExtensionKind, Map<String, TaxonomyExtension>> immutable =
new EnumMap<>(ExtensionKind.class);
registrations.forEach((kind, values) -> immutable.put(kind, Map.copyOf(values)));
this.byKind = Map.copyOf(immutable);
}
public List<ExtensionDescriptor> listAll() {
return byKind.keySet().stream()
.sorted()
.flatMap(kind -> listByKind(kind).stream())
.toList();
}
public List<ExtensionDescriptor> listByKind(ExtensionKind kind) {
if (kind == null) {
return List.of();
}
return byKind.getOrDefault(kind, Map.of()).values().stream()
.map(TaxonomyExtension::extensionDescriptor)
.sorted(Comparator.comparing(
(ExtensionDescriptor descriptor) -> normalize(descriptor.id()))
.thenComparing(ExtensionDescriptor::id))
.toList();
}
public Optional<ExtensionDescriptor> findDescriptor(ExtensionKind kind, String id) {
if (kind == null || id == null || id.isBlank()) {
return Optional.empty();
}
return Optional.ofNullable(byKind.getOrDefault(kind, Map.of()).get(normalize(id)))
.map(TaxonomyExtension::extensionDescriptor);
}
public ExtensionDescriptor getRequiredDescriptor(ExtensionKind kind, String id) {
return findDescriptor(kind, id)
.orElseThrow(() -> new IllegalArgumentException(
"Unknown extension %s/%s".formatted(kind, id)));
}
private Registration validatedRegistration(TaxonomyExtension extension) {
if (extension == null) {
throw new IllegalStateException("A null TaxonomyExtension was passed to the registry");
}
if (extension.kind() == null) {
throw new IllegalStateException(
"Extension %s returned a null kind".formatted(extension.getClass().getName()));
}
if (extension.id() == null || extension.id().isBlank()) {
throw new IllegalStateException(
"Extension %s must declare a non-blank ID"
.formatted(extension.getClass().getName()));
}
if (extension.displayName() == null || extension.displayName().isBlank()) {
throw new IllegalStateException(
"Extension %s must declare a non-blank display name"
.formatted(extension.getClass().getName()));
}
if (extension.description() == null) {
throw new IllegalStateException(
"Extension %s must declare a non-null description"
.formatted(extension.getClass().getName()));
}
return new Registration(extension.kind(), normalize(extension.id()), extension);
}
private String normalize(String id) {
return id.trim().toLowerCase(Locale.ROOT);
}
private record Registration(
ExtensionKind kind, String normalizedId, TaxonomyExtension extension) {
}
}