DefaultEncoderFactory.java

/*
 * SPDX-FileCopyrightText: 2025 Lucimber UG
 * SPDX-License-Identifier: Apache-2.0
 */
package com.lucimber.dbus.codec.encoder;

import com.lucimber.dbus.type.DBusType;
import com.lucimber.dbus.type.Type;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;

/**
 * Default implementation of EncoderFactory that provides encoders for all standard D-Bus types.
 *
 * <p>This factory uses a registry-based approach to create encoders, making it easy to extend with
 * new types and improving performance by avoiding switch statements.
 */
public final class DefaultEncoderFactory implements EncoderFactory {

    private final Map<Type, EncoderCreator> encoderRegistry;

    /** Creates a new DefaultEncoderFactory with all standard D-Bus encoders registered. */
    public DefaultEncoderFactory() {
        this.encoderRegistry = new EnumMap<>(Type.class);
        registerStandardEncoders();
    }

    private void registerStandardEncoders() {
        // Basic types
        encoderRegistry.put(Type.BOOLEAN, BooleanEncoder::new);
        encoderRegistry.put(Type.BYTE, order -> new ByteEncoder());
        encoderRegistry.put(Type.DOUBLE, DoubleEncoder::new);
        encoderRegistry.put(Type.INT16, Int16Encoder::new);
        encoderRegistry.put(Type.INT32, Int32Encoder::new);
        encoderRegistry.put(Type.INT64, Int64Encoder::new);
        encoderRegistry.put(Type.UINT16, UInt16Encoder::new);
        encoderRegistry.put(Type.UINT32, UInt32Encoder::new);
        encoderRegistry.put(Type.UINT64, UInt64Encoder::new);
        encoderRegistry.put(Type.STRING, StringEncoder::new);
        encoderRegistry.put(Type.OBJECT_PATH, ObjectPathEncoder::new);
        encoderRegistry.put(Type.SIGNATURE, order -> new SignatureEncoder());
        encoderRegistry.put(Type.UNIX_FD, UnixFdEncoder::new);

        // Note: Container types (ARRAY, DICT_ENTRY, STRUCT) are not registered here
        // because they require additional signature parameters that this basic factory cannot
        // provide.
        // Only VARIANT is supported as it doesn't require external signature information.
        encoderRegistry.put(Type.VARIANT, VariantEncoder::new);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Encoder<DBusType, ByteBuffer> createEncoder(Type type, ByteOrder order)
            throws EncoderException {
        Objects.requireNonNull(type, "type must not be null");
        Objects.requireNonNull(order, "order must not be null");

        EncoderCreator creator = encoderRegistry.get(type);
        if (creator == null) {
            throw new EncoderException("No encoder available for type: " + type);
        }

        return (Encoder<DBusType, ByteBuffer>) creator.create(order);
    }

    @Override
    public boolean canEncode(Type type) {
        return encoderRegistry.containsKey(type);
    }

    /**
     * Registers a custom encoder creator for a specific type.
     *
     * @param type the D-Bus type
     * @param creator the encoder creator function
     */
    public void registerEncoder(Type type, EncoderCreator creator) {
        Objects.requireNonNull(type, "type must not be null");
        Objects.requireNonNull(creator, "creator must not be null");
        encoderRegistry.put(type, creator);
    }

    /** Functional interface for creating encoders with a given byte order. */
    @FunctionalInterface
    public interface EncoderCreator {
        Encoder<?, ByteBuffer> create(ByteOrder order) throws EncoderException;
    }
}