IntegrationJson.java

package com.taxonomy.interop;

import com.taxonomy.exchange.ReqifExchangeCodec;
import org.springframework.stereotype.Component;
import tools.jackson.databind.ObjectMapper;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

@Component
public class IntegrationJson {
    private final ObjectMapper mapper;
    public IntegrationJson(ObjectMapper mapper) { this.mapper = mapper; }
    public String write(Object value) {
        try { return mapper.writeValueAsString(value); }
        catch (Exception failure) { throw new IllegalStateException("Cannot encode integration evidence", failure); }
    }
    public <T> T read(String value, Class<T> type) {
        if (value == null) return null;
        try { return mapper.readValue(value, type); }
        catch (Exception failure) { throw new IllegalStateException("Cannot decode versioned integration evidence", failure); }
    }
    public String fingerprint(Object value) { return ReqifExchangeCodec.digest(write(canonical(value)).getBytes(StandardCharsets.UTF_8)); }
    private Object canonical(Object value) {
        if (value == null || value instanceof String || value instanceof Number || value instanceof Boolean) return value;
        if (value instanceof Enum<?> e) return e.name();
        if (value instanceof Map<?, ?> map) {
            Map<String, Object> result = new TreeMap<>(); map.forEach((key, item) -> result.put(key.toString(), canonical(item))); return result;
        }
        if (value instanceof Collection<?> collection) {
            var result = new ArrayList<>(collection.stream().map(this::canonical).toList());
            if (value instanceof Set<?>) result.sort(java.util.Comparator.comparing(this::write)); return result;
        }
        if (value.getClass().isRecord()) {
            Map<String, Object> result = new TreeMap<>();
            try { for (var field : value.getClass().getRecordComponents()) result.put(field.getName(), canonical(field.getAccessor().invoke(value))); }
            catch (ReflectiveOperationException failure) { throw new IllegalStateException("Cannot fingerprint integration value", failure); }
            return result;
        }
        return value.toString();
    }
}