FactoryRegistry.java
package com.morphiqlabs.wavelet.api;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* Central registry for factory implementations in the VectorWave library.
*
* <p>This registry provides a centralized location to register and retrieve
* factory instances, enabling dependency injection and flexible factory
* management across the library.</p>
*
* <h2>Usage Example</h2>
* <pre>{@code
* // Register factories at application startup
* FactoryRegistry registry = FactoryRegistry.getInstance();
* registry.register("waveletOps", WaveletOpsFactory.getInstance());
* registry.register("cwtTransform", CWTFactory.getInstance());
*
* // Retrieve factories later
* Factory<WaveletOps, TransformConfig> opsFactory =
* registry.getFactory("waveletOps", WaveletOps.class, TransformConfig.class)
* .orElseThrow(() -> new IllegalStateException("Factory not found"));
*
* // Use the factory
* WaveletOps ops = opsFactory.create();
* }</pre>
*
* <h2>Thread Safety</h2>
* <p>This registry is thread-safe and can be safely accessed from multiple
* threads concurrently.</p>
*/
public final class FactoryRegistry {
private static final FactoryRegistry INSTANCE = new FactoryRegistry();
private final Map<String, Factory<?, ?>> factories = new ConcurrentHashMap<>();
/**
* Private constructor for singleton pattern.
*/
private FactoryRegistry() {
// Initialize with default factories
initializeDefaultFactories();
}
/**
* Gets the singleton instance of the factory registry.
*
* @return the registry instance
*/
public static FactoryRegistry getInstance() {
return INSTANCE;
}
/**
* Registers a factory with the given key.
*
* @param key the unique key for this factory
* @param factory the factory instance
* @throws IllegalArgumentException if key is null or empty
* @throws IllegalStateException if a factory is already registered with this key
*/
public void register(String key, Factory<?, ?> factory) {
if (key == null || key.trim().isEmpty()) {
throw new IllegalArgumentException("Factory key cannot be null or empty");
}
if (factory == null) {
throw new IllegalArgumentException(
String.format("Cannot register null factory for key '%s'", key));
}
Factory<?, ?> existing = factories.putIfAbsent(key, factory);
if (existing != null) {
throw new IllegalStateException(
String.format("Factory already registered with key '%s'. " +
"Existing factory: %s (type: %s), " +
"Attempted to register: %s (type: %s)",
key,
existing.getDescription(), existing.getClass().getName(),
factory.getDescription(), factory.getClass().getName()));
}
}
/**
* Retrieves a factory by its key with type safety.
*
* @param key the factory key
* @param productType the expected product type
* @param configType the expected configuration type
* @param <T> the product type
* @param <C> the configuration type
* @return an Optional containing the factory if found and types match
*/
@SuppressWarnings("unchecked")
public <T, C> Optional<Factory<T, C>> getFactory(String key, Class<T> productType, Class<C> configType) {
Factory<?, ?> factory = factories.get(key);
if (factory == null) {
return Optional.empty();
}
// Type checking would require additional metadata
// For now, we trust the caller to request the correct types
try {
return Optional.of((Factory<T, C>) factory);
} catch (ClassCastException e) {
return Optional.empty();
}
}
/**
* Retrieves a factory by its key without type checking.
*
* @param key the factory key
* @return an Optional containing the factory if found
*/
public Optional<Factory<?, ?>> getFactory(String key) {
return Optional.ofNullable(factories.get(key));
}
/**
* Checks if a factory is registered with the given key.
*
* @param key the factory key
* @return true if a factory is registered with this key
*/
public boolean isRegistered(String key) {
return factories.containsKey(key);
}
/**
* Unregisters a factory.
*
* @param key the factory key
* @return true if the factory was removed, false if not found
*/
public boolean unregister(String key) {
return factories.remove(key) != null;
}
/**
* Gets all registered factory keys.
*
* @return an unmodifiable set of factory keys
*/
public Set<String> getRegisteredKeys() {
return Set.copyOf(factories.keySet());
}
/**
* Clears all registered factories except the defaults.
*/
public void clear() {
factories.clear();
initializeDefaultFactories();
}
/**
* Initializes the default factories that are always available.
*/
private void initializeDefaultFactories() {
// Register default factories
try {
// These will be registered lazily when the classes are loaded
// to avoid circular dependencies during initialization
} catch (Exception e) {
// Ignore initialization errors
}
}
/**
* Registers the default VectorWave factories.
* This method should be called during application initialization.
*/
public static void registerDefaults() {
FactoryRegistry registry = getInstance();
// Register the default factories if they haven't been registered yet
// WaveletTransformFactory temporarily disabled during DWT -> MODWT migration
/*
if (!registry.isRegistered("waveletTransform")) {
// WaveletTransformFactory is instance-based, so we create a shared instance
try {
registry.register("waveletTransform",
new com.morphiqlabs.wavelet.WaveletTransformFactory());
} catch (Exception e) {
throw new IllegalStateException(
"Failed to register default WaveletTransformFactory with key 'waveletTransform': " +
e.getMessage(), e);
}
}
*/
if (!registry.isRegistered("cwtTransform")) {
try {
registry.register("cwtTransform",
com.morphiqlabs.wavelet.cwt.CWTFactory.getInstance());
} catch (Exception e) {
throw new IllegalStateException(
"Failed to register default CWTFactory with key 'cwtTransform': " +
e.getMessage(), e);
}
}
// Streaming factories temporarily disabled during DWT -> MODWT migration
/*
if (!registry.isRegistered("streamingDenoiser")) {
try {
registry.register("streamingDenoiser",
com.morphiqlabs.wavelet.streaming.StreamingDenoiserFactory.getInstance());
} catch (Exception e) {
throw new IllegalStateException(
"Failed to register default StreamingDenoiserFactory with key 'streamingDenoiser': " +
e.getMessage(), e);
}
}
*/
}
}