package io.fluxcapacitor.common.api;

import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Value;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;

@Value
public class Metadata implements Map<String, String> {
    Map<String, String> entries;

    @JsonCreator
    private Metadata(Map<String, String> entries) {
        this.entries = new HashMap<>(entries);
    }

    public static Metadata empty() {
        return new Metadata(emptyMap());
    }

    public static Metadata from(String key, String value) {
        return new Metadata(singletonMap(key, value));
    }

    public static Metadata from(Map<String, String> map) {
        return new Metadata(map);
    }

    @Override
    public int size() {
        return entries.size();
    }

    @Override
    public boolean isEmpty() {
        return entries.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return entries.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return entries.containsValue(value);
    }

    @Override
    public String get(Object key) {
        return entries.get(key);
    }

    @Override
    public String put(String key, String value) {
        return entries.put(key, value);
    }

    @Override
    public String remove(Object key) {
        return entries.remove(key);
    }

    @Override
    public void putAll(Map<? extends String, ? extends String> m) {
        entries.putAll(m);
    }

    @Override
    public void clear() {
        entries.clear();
    }

    @Override
    public Set<String> keySet() {
        return entries.keySet();
    }

    @Override
    public Collection<String> values() {
        return entries.values();
    }

    @Override
    public Set<Entry<String, String>> entrySet() {
        return entries.entrySet();
    }

}

@@ -95,19 +95,136 @@
 ort 
-lombok.Valu
+com.fasterxml.jackson.databind.ObjectMapper;%0Aimport lombok.SneakyThrows;%0Aimport lombok.Value;%0Aimport lombok.experimental.Delegat
 e;%0A%0A
@@ -231,35 +231,34 @@
 import java.
-util.Collec
+io.IOExcep
 tion;%0Aimport
@@ -320,172 +320,544 @@
 til.
-Set;%0A%0Aimport static java.util.Collections.emptyMap;%0Aimport static java.util.Collections.singletonMap;%0A%0A@Value%0Apublic class Metadata implements Map%3CString, String%3E %7B
+Optional;%0A%0Aimport static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;%0Aimport static com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS;%0Aimport static java.util.Collections.emptyMap;%0Aimport static java.util.Collections.singletonMap;%0A%0A@Value%0Apublic class Metadata implements Map%3CString, String%3E %7B%0A    public static ObjectMapper objectMapper = new ObjectMapper()%0A            .findAndRegisterModules().disable(FAIL_ON_EMPTY_BEANS).disable(FAIL_ON_UNKNOWN_PROPERTIES);%0A%0A    @Delegate
 %0A   
@@ -1326,220 +1326,67 @@
    @
-Override%0A    public int size() %7B%0A        return entries.size();%0A    %7D%0A%0A    @Override%0A    public boolean isEmpty() %7B%0A        return entries.isEmpty();%0A    %7D%0A%0A    @Override%0A    public boolean containsKey(
+SneakyThrows%0A    public String put(String key, 
 Object 
-key
+value
 ) %7B%0A
@@ -1404,147 +1404,56 @@
 urn 
-entries.containsKey(key);%0A    %7D%0A%0A    @Override%0A    public boolean containsValue(Object value) %7B%0A        return entries.containsValue
+put(key, objectMapper.writeValueAsString
 (value)
+)
 ;%0A  
@@ -1466,201 +1466,205 @@
    @
-Override%0A    public String get(Object key) %7B%0A        return entries.get(key);
+SneakyThrows%0A    public %3CT%3E T get(String key, Class%3CT%3E type) %7B%0A        return Optional.ofNullable(get(key)).map(v -%3E %7B
 %0A    
-%7D%0A%0A
     
-@Override%0A    public String put(String key, String value) %7B%0A        return entries.put(key, valu
+    try %7B%0A                return objectMapper.readValue(v, typ
 e);%0A    
 %7D%0A%0A 
@@ -1663,575 +1663,290 @@
     
-%7D%0A%0A
     
-@Override%0A    public String remove(Object key) %7B%0A        return entries.remove(key);%0A    %7D%0A%0A    @Override%0A    public void putAll(Map%3C? extends String, ? extends String%3E m) %7B%0A        entries.putAll(m);%0A    %7D%0A%0A    @Override%0A    public void clear() %7B%0A        entries.clear();%0A
+    %7D catch (IOException e) %7B%0A                throw new IllegalStateException(String.format(%22Failed to deserialize value %25s to a %25s for key %25s%22,%0A               
     
-%7D%0A%0A
     
-@Override%0A    public Set%3CString%3E keySet() %7B%0A        return entries.keySet();%0A
+                
     
-%7D%0A%0A
     
-@Override%0A    public Collection%3CString%3E values() %7B%0A        return entries.values();%0A    %7D%0A%0A    @Override%0A    public Set%3CEntry%3CString, String%3E%3E entrySet() %7B%0A        return entries.entrySet(
+               v, type.getSimpleName(), key), e);%0A            %7D%0A        %7D).orElse(null
 );%0A 

Add convenience methods to Metadata that automatically (de)serialize objects