package com.hubspot.jackson.datatype.protobuf;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.google.protobuf.Message;
import com.hubspot.jackson.datatype.protobuf.builtin.deserializers.MessageDeserializer;

public class MessageDeserializerFactory extends Deserializers.Base {
  private final ProtobufJacksonConfig config;
  private final ConcurrentMap<Class<? extends Message>, ProtobufDeserializer<?, ?>> deserializerCache;

  /**
   * @deprecated use {@link #MessageDeserializerFactory(ProtobufJacksonConfig)} instead
   */
  @Deprecated
  public MessageDeserializerFactory(ExtensionRegistryWrapper extensionRegistry) {
    this(ProtobufJacksonConfig.builder().extensionRegistry(extensionRegistry).build());
  }

  public MessageDeserializerFactory(ProtobufJacksonConfig config) {
    this.config = config;
    this.deserializerCache = new ConcurrentHashMap<>();
  }

  @Override
  @SuppressWarnings("unchecked")
  public JsonDeserializer<?> findBeanDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc)
          throws JsonMappingException {
    if (Message.class.isAssignableFrom(type.getRawClass())) {
      return getDeserializer((Class<? extends Message>) type.getRawClass()).buildAtEnd();
    } else if (Message.Builder.class.isAssignableFrom(type.getRawClass())) {
      return getDeserializer((Class<? extends Message>) type.getRawClass().getDeclaringClass());
    } else {
      return super.findBeanDeserializer(type, config, beanDesc);
    }
  }

  @SuppressWarnings("unchecked")
  private <T extends Message> ProtobufDeserializer<T, ?> getDeserializer(Class<T> messageType) {
    ProtobufDeserializer<?, ?> deserializer = deserializerCache.get(messageType);
    if (deserializer == null) {
      ProtobufDeserializer<T, ?> newDeserializer = new MessageDeserializer<>(messageType, config);
      ProtobufDeserializer<?, ?> previousDeserializer = deserializerCache.putIfAbsent(messageType, newDeserializer);
      deserializer = previousDeserializer == null ? newDeserializer : previousDeserializer;
    }

    return (ProtobufDeserializer<T, ?>) deserializer;
  }
}

@@ -2134,143 +2134,73 @@
     
-ProtobufDeserializer%3CT, ?%3E newDeserializer = new MessageDeserializer%3C%3E(messageType, config);%0A      ProtobufDeserializer%3C?, ?%3E previousD
+// use computeIfAbsent as a fallback because it allocates%0A      d
 eser
@@ -2227,19 +2227,23 @@
 erCache.
+com
 put
+e
 IfAbsent
@@ -2243,16 +2243,27 @@
 fAbsent(
+%0A          
 messageT
@@ -2270,117 +2270,84 @@
 ype,
- newDeserializer);%0A      deserializer = previousDeserializer == null ? newDeserializer : previousDeserializer
+%0A          ignored -%3E new MessageDeserializer%3C%3E(messageType, config)%0A      )
 ;%0A  

Use computeIfAbsent