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

io.avaje.jsonb.generator.ValueReader Maven / Gradle / Ivy

package io.avaje.jsonb.generator;

import java.util.Set;
import java.util.TreeSet;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;

final class ValueReader implements BeanReader {

  private final ExecutableElement method;
  private final TypeElement element;
  private final String shortName;
  private final String type;
  private final Set importTypes = new TreeSet<>();
  private final String returnTypeStr;
  private final GenericType genericType;
  private final String adapterShortType;
  private final boolean isEnum;
  private final ExecutableElement constructor;

  ValueReader(TypeElement beanType, ExecutableElement e) {
    this.method = e;
    this.element = beanType;
    this.isEnum = beanType.getKind() == ElementKind.ENUM;
    final TypeMirror returnType = e.getReturnType();
    this.returnTypeStr = PrimitiveUtil.wrap(Util.shortType(returnType.toString()));
    this.type = Util.trimAnnotations(returnType.toString());
    this.shortName = shortName(beanType);

    genericType = GenericType.parse(returnType.toString());
    final String shortType = genericType.shortType();
    adapterShortType = "JsonAdapter<" + PrimitiveUtil.wrap(Util.shortType(shortType)) + ">";

    this.constructor = beanType.getEnclosedElements().stream()
      .filter(CreatorPrism::isPresent)
      .findFirst()
      .map(ExecutableElement.class::cast)
      .or(() -> ElementFilter.constructorsIn(element.getEnclosedElements()).stream()
        .filter(s -> s.getParameters().size() == 1)
        .filter(s -> Util.trimAnnotations(returnType.toString())
          .equals(Util.trimAnnotations(s.getParameters().get(0).asType().toString())))
        .findFirst())
      .orElse(null);
  }

  @Override
  public void read() {
    // nothing to read here
  }

  @Override
  public void cascadeTypes(Set extraTypes) {
    extraTypes.add(returnTypeStr);
  }

  @Override
  public String toString() {
    return element.toString();
  }

  @Override
  public String shortName() {
    return shortName;
  }

  @Override
  public TypeElement beanType() {
    return element;
  }

  private Set importTypes() {
    importTypes.add(Constants.JSONB_WILD);
    importTypes.add(Constants.IOEXCEPTION);
    importTypes.add(Constants.JSONB_SPI);
    importTypes.add("java.util.EnumMap");
    importTypes.add("java.util.HashMap");
    importTypes.add("java.util.Map");
    if (Util.validImportType(type)) {
      importTypes.add(type);
    }
    importTypes.add(Constants.JSONB_SPI);
    importTypes.add(element.asType().toString());
    importTypes.add(method.getReturnType().toString());
    return importTypes;
  }

  @Override
  public void writeImports(Append writer) {
    for (final String importType : importTypes()) {
      if (Util.validImportType(importType)) {
        writer.append("import %s;", Util.sanitizeImports(importType)).eol();
      }
    }
    writer.eol();
  }

  @Override
  public void writeFields(Append writer) {
    if (isEnum) {
      writer.append("  private static final Map<%s, %s> toValue = new EnumMap<>(%s.class);", shortName, returnTypeStr, shortName).eol();
      writer.append("  private static final Map<%s, %s> toEnum = new HashMap<>();", returnTypeStr, shortName).eol();
    }
    writer.append("  private final %s adapter;", adapterShortType).eol();
    writer.eol();
  }

  @Override
  public void writeConstructor(Append writer) {
    writer.append("    this.adapter = jsonb.adapter(%s);", genericType.asTypeDeclaration().replace("? extends ", "")).eol();
    if (isEnum) {
      writer.append("    if(!toValue.isEmpty()) return;").eol();
      writer.append("    for(final var enumConst : %s.values()) {", shortName).eol();
      writer.append("      var val = enumConst.%s();", method.getSimpleName()).eol();
      writer.append("      toValue.put(enumConst, val);").eol();
      writer.append("      if(toEnum.containsKey(val)) throw new IllegalArgumentException(\"Duplicate value \"+ val + \" from enum method %s. @Json.Value methods must return unique values\");",method.getSimpleName()).eol();
      writer.append("      toEnum.put(val, enumConst);").eol();
      writer.append("    }").eol();
    }
  }

  @Override
  public void writeToJson(Append writer) {
    writer.eol();
    writer.append("  @Override").eol();
    writer.append("  public void toJson(JsonWriter writer, %s value) {", shortName).eol();
    if (isEnum) {
      writer.append("    adapter.toJson(writer, toValue.get(value));").eol();
    } else {
      writer.append("    adapter.toJson(writer, value.%s());", method.getSimpleName()).eol();
    }
    writer.append("  }").eol();
  }

  @Override
  public void writeFromJson(Append writer) {
    final String varName = Util.initLower(shortName);
    writer.eol();
    writer.append("  @Override").eol();
    writer.append("  public %s fromJson(JsonReader reader) {", shortName).eol();

    if (!isEnum) {
      var constructMethod =
          constructor.getKind() == ElementKind.CONSTRUCTOR
              ? "new " + shortName
              : shortName + "." + constructor.getSimpleName();

      writer.append("    return %s(adapter.fromJson(reader));", constructMethod).eol();

    } else {
      writer.append("    final var value = adapter.fromJson(reader);").eol();
      writer.append("    final var enumConstant = toEnum.get(value);").eol();
      writer.append("    if (enumConstant == null) ", varName).eol();
      writer.append("      throw new JsonDataException(\"Unable to determine %s enum value for \" + value);",shortName).eol();
      writer.append("    return enumConstant;").eol();
    }

    writer.append("  }").eol();
  }

  @Override
  public boolean supportsViewBuilder() {
    return false;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy