All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hubspot.rosetta.internal.RosettaAnnotationIntrospector Maven / Gradle / Ivy

The newest version!
package com.hubspot.rosetta.internal;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonInclude.Value;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.hubspot.rosetta.annotations.RosettaCreator;
import com.hubspot.rosetta.annotations.RosettaDeserializationProperty;
import com.hubspot.rosetta.annotations.RosettaDeserialize;
import com.hubspot.rosetta.annotations.RosettaIgnore;
import com.hubspot.rosetta.annotations.RosettaNaming;
import com.hubspot.rosetta.annotations.RosettaProperty;
import com.hubspot.rosetta.annotations.RosettaSerializationProperty;
import com.hubspot.rosetta.annotations.RosettaSerialize;
import com.hubspot.rosetta.annotations.RosettaValue;
import com.hubspot.rosetta.annotations.StoredAsJson;
import java.util.Optional;
import java.util.function.Supplier;

public class RosettaAnnotationIntrospector extends NopAnnotationIntrospector {

  private static final long serialVersionUID = 1L;

  private final ObjectMapper objectMapper;

  public RosettaAnnotationIntrospector(ObjectMapper objectMapper) {
    this.objectMapper = objectMapper;
  }

  @Override
  public Object findNamingStrategy(AnnotatedClass ac) {
    RosettaNaming ann = ac.getAnnotation(RosettaNaming.class);
    return ann == null ? null : ann.value();
  }

  @Override
  @SuppressWarnings("unchecked")
  public JsonSerializer findSerializer(Annotated a) {
    StoredAsJson storedAsJson = a.getAnnotation(StoredAsJson.class);
    RosettaSerialize rosettaSerialize = a.getAnnotation(RosettaSerialize.class);
    if (storedAsJson != null && rosettaSerialize != null) {
      throw new IllegalArgumentException(
        "Cannot have @StoredAsJson as well as @RosettaSerialize annotations on the same entry"
      );
    }
    if (storedAsJson != null) {
      Class type = a.getRawType();
      return storedAsJson.binary()
        ? new StoredAsJsonBinarySerializer(type)
        : new StoredAsJsonSerializer(type);
    }

    if (rosettaSerialize != null) {
      Class klass = rosettaSerialize.using();
      if (klass != JsonSerializer.None.class) {
        return ClassUtil.createInstance(
          klass,
          objectMapper.getSerializationConfig().canOverrideAccessModifiers()
        );
      }
    }
    return null;
  }

  @Override
  @SuppressWarnings("unchecked")
  public JsonDeserializer findDeserializer(Annotated a) {
    StoredAsJson storedAsJson = a.getAnnotation(StoredAsJson.class);
    RosettaDeserialize rosettaDeserialize = a.getAnnotation(RosettaDeserialize.class);
    if (storedAsJson != null && rosettaDeserialize != null) {
      throw new IllegalArgumentException(
        "Cannot have @StoredAsJson as well as @RosettaDeserialize annotations on the same entry"
      );
    }
    if (storedAsJson != null) {
      if (a instanceof AnnotatedMethod) {
        a = getAnnotatedTypeFromAnnotatedMethod((AnnotatedMethod) a);
      }

      String empty = StoredAsJson.NULL.equals(storedAsJson.empty())
        ? "null"
        : storedAsJson.empty();
      return new StoredAsJsonDeserializer(
        a.getRawType(),
        a.getType(),
        empty,
        objectMapper
      );
    }

    if (rosettaDeserialize != null) {
      Class klass = rosettaDeserialize.using();
      if (klass != JsonDeserializer.None.class) {
        return ClassUtil.createInstance(
          klass,
          objectMapper.getDeserializationConfig().canOverrideAccessModifiers()
        );
      }
    }

    return null;
  }

  @Override
  public boolean hasAsValueAnnotation(AnnotatedMethod am) {
    return am.hasAnnotation(RosettaValue.class);
  }

  @Override
  public boolean hasCreatorAnnotation(Annotated a) {
    return a.hasAnnotation(RosettaCreator.class);
  }

  @Override
  public PropertyName findNameForSerialization(Annotated a) {
    return getFirstNonEmpty(
        () -> findRosettaGetterName(a),
        () -> findRosettaPropertyName(a),
        () -> Optional.ofNullable(super.findNameForSerialization(a))
      )
      .orElse(null);
  }

  @Override
  public PropertyName findNameForDeserialization(Annotated a) {
    return getFirstNonEmpty(
        () -> findRosettaSetterName(a),
        () -> findRosettaPropertyName(a),
        () -> Optional.ofNullable(super.findNameForDeserialization(a))
      )
      .orElse(null);
  }

  @Override
  public Include findSerializationInclusion(Annotated a, Include defValue) {
    return Include.ALWAYS;
  }

  @Override
  public Include findSerializationInclusionForContent(Annotated a, Include defValue) {
    return Include.ALWAYS;
  }

  @Override
  public Value findPropertyInclusion(Annotated a) {
    return Value.construct(Include.ALWAYS, Include.ALWAYS);
  }

  @Override
  public boolean hasIgnoreMarker(AnnotatedMember m) {
    return m.hasAnnotation(RosettaIgnore.class);
  }

  @Override
  public Boolean hasAsValue(Annotated a) {
    if (a.hasAnnotation(RosettaIgnore.class)) {
      // The super method can return null, so we can't use && here
      return false;
    } else {
      return super.hasAsValue(a);
    }
  }

  @Override
  public Version version() {
    return Version.unknownVersion();
  }

  private Optional findRosettaPropertyName(Annotated a) {
    return Optional
      .ofNullable(a.getAnnotation(RosettaProperty.class))
      .map(annotation ->
        annotation.value().isEmpty()
          ? PropertyName.USE_DEFAULT
          : new PropertyName(annotation.value())
      );
  }

  private Optional findRosettaGetterName(Annotated a) {
    return Optional
      .ofNullable(a.getAnnotation(RosettaSerializationProperty.class))
      .map(annotation ->
        annotation.value().isEmpty()
          ? PropertyName.USE_DEFAULT
          : new PropertyName(annotation.value())
      );
  }

  private Optional findRosettaSetterName(Annotated a) {
    return Optional
      .ofNullable(a.getAnnotation(RosettaDeserializationProperty.class))
      .map(annotation ->
        annotation.value().isEmpty()
          ? PropertyName.USE_DEFAULT
          : new PropertyName(annotation.value())
      );
  }

  private Annotated getAnnotatedTypeFromAnnotatedMethod(AnnotatedMethod a) {
    if (a.getParameterCount() > 0) {
      return a.getParameter(0);
    } else if (a.hasReturnType()) {
      return a;
    } else {
      throw new IllegalArgumentException(
        "Cannot have @StoredAsJson on a method with no parameters AND no arguments"
      );
    }
  }

  private  Optional getFirstNonEmpty(Supplier>... suppliers) {
    for (Supplier> supplier : suppliers) {
      Optional maybeValue = supplier.get();
      if (maybeValue.isPresent()) {
        return maybeValue;
      }
    }
    return Optional.empty();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy