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

com.ppiech.auto.value.jackson.processor.AutoValueJacksonExtension Maven / Gradle / Ivy

The newest version!
package com.ppiech.auto.value.jackson.processor;

import com.ppiech.auto.value.jackson.JacksonAuto;
import com.ppiech.auto.value.jackson.JsonMapper;
import com.ppiech.auto.value.jackson.LoganSquare;
import com.ppiech.auto.value.jackson.processor.type.TypeUtils;
import com.ppiech.auto.value.jackson.processor.type.field.TypeConverterFieldType;
import com.ppiech.auto.value.jackson.typeconverters.TypeConverter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.google.auto.common.MoreTypes;
import com.google.auto.service.AutoService;
import com.google.auto.value.extension.AutoValueExtension;
import com.google.common.base.CaseFormat;
import com.google.common.base.Defaults;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.primitives.Primitives;
import com.ppiech.auto.value.jackson.processor.type.Type;
import com.ppiech.auto.value.jackson.annotation.JsonProperty;
import com.squareup.javapoet.*;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.*;

import static javax.lang.model.element.Modifier.*;

@AutoService(AutoValueExtension.class)
public class AutoValueJacksonExtension extends AutoValueExtension {

  /**
   * Compiler flag to indicate that collections/maps should default to their empty forms. Default is to default to null.
   */
  static final String COLLECTIONS_DEFAULT_TO_EMPTY = "autovaluegson.defaultCollectionsToEmpty";

  public static class Property {
    final String methodName;
    final String humanName;
    final ExecutableElement element;
    final TypeName typeName;
    final ImmutableSet annotations;
    final TypeMirror typeConverter;
    final Type type;

    public Property(String humanName, ExecutableElement element, Elements elements, Types types) {
      this.methodName = element.getSimpleName().toString();
      this.humanName = humanName;
      this.element = element;

      typeName = TypeName.get(element.getReturnType());
      annotations = buildAnnotations(element);

      typeConverter = getAnnotationProperty(element, JsonProperty.class, "typeConverter");
      type = Type.typeFor(element.getReturnType(), typeConverter, elements, types);
    }

    public static TypeMirror getAnnotationProperty(Element foo, Class annotation, String key) {
      AnnotationMirror am = getAnnotationMirror(foo, annotation);
      if (am == null) {
        return null;
      }
      AnnotationValue av = getAnnotationProperty(am, key);
      return av == null ? null : (TypeMirror) av.getValue();
    }

    private static AnnotationMirror getAnnotationMirror(Element typeElement, Class clazz) {
      String clazzName = clazz.getName();
      for (AnnotationMirror m : typeElement.getAnnotationMirrors()) {
        if (m.getAnnotationType().toString().equals(clazzName)) {
          return m;
        }
      }
      return null;
    }

    private static AnnotationValue getAnnotationProperty(AnnotationMirror annotationMirror, String key) {
      Map values = annotationMirror.getElementValues();
      for (Map.Entry entry : values.entrySet()) {
        if (entry.getKey().getSimpleName().toString().equals(key)) {
          return entry.getValue();
        }
      }
      return null;
    }

    public String[] jsonNames() {
      JsonProperty jsonMethod = element.getAnnotation(JsonProperty.class);
      if (jsonMethod != null && jsonMethod.name().length != 0) {
        return jsonMethod.name();
      } else {
        return new String[]{humanName};
      }
    }

    public Boolean nullable() {
      return annotations.contains("Nullable");
    }

    private ImmutableSet buildAnnotations(ExecutableElement element) {
      ImmutableSet.Builder builder = ImmutableSet.builder();

      List annotations = element.getAnnotationMirrors();
      for (AnnotationMirror annotation : annotations) {
        builder.add(annotation.getAnnotationType().asElement().getSimpleName().toString());
      }

      return builder.build();
    }
  }

  @Override
  public boolean applicable(Context context) {
    // check that the class contains a public static method returning a TypeAdapter
    TypeElement type = context.autoValueClass();
    TypeName typeName = TypeName.get(type.asType());

    // not supporting classes with generics
    Messager messager = context.processingEnvironment().getMessager();
    if (typeName instanceof ParameterizedTypeName) {
      messager.printMessage(Diagnostic.Kind.WARNING,
          String.format("Found public static method on class %s with type parameters."
              + "Skipping GsonTypeAdapter generation.", type));
      return false;
    }

    ParameterizedTypeName jsonMapperType = ParameterizedTypeName.get(
        ClassName.get(JsonMapper.class), typeName);
    TypeName returnedTypeAdapter = null;
    for (ExecutableElement method : ElementFilter.methodsIn(type.getEnclosedElements())) {
      if (method.getModifiers().contains(Modifier.STATIC)
          && method.getModifiers().contains(Modifier.PUBLIC)) {
        TypeMirror rType = method.getReturnType();
        TypeName returnType = TypeName.get(rType);
        if (returnType.equals(jsonMapperType)) {
          return true;
        }

        if (returnType.equals(jsonMapperType.rawType)
            || (returnType instanceof ParameterizedTypeName
            && ((ParameterizedTypeName) returnType).rawType.equals(jsonMapperType.rawType))) {
          returnedTypeAdapter = returnType;
        }
      }
    }

    if (returnedTypeAdapter == null) {
      return false;
    }

    // emit a warning if the user added a method returning a JsonMapper, but not of the right type
    if (returnedTypeAdapter instanceof ParameterizedTypeName) {
      ParameterizedTypeName paramReturnType = (ParameterizedTypeName) returnedTypeAdapter;
      TypeName argument = paramReturnType.typeArguments.get(0);

      // If the original type uses generics, user's don't have to nest the generic type args
      if (typeName instanceof ParameterizedTypeName) {
        ParameterizedTypeName pTypeName = (ParameterizedTypeName) typeName;
        if (pTypeName.rawType.equals(argument)) {
          return true;
        }
      } else {
        messager.printMessage(Diagnostic.Kind.WARNING,
            String.format("Found public static method returning JsonMapper<%s> on %s class. "
                + "Skipping JsonObjectMapper generation.", argument, type));
      }
    } else {
      messager.printMessage(Diagnostic.Kind.WARNING, "Found public static method returning "
          + "JsonMapper with no type arguments, skipping JsonObjectMapper generation.");
    }

    return false;
  }

  @Override
  public String generateClass(Context context, String className, String classToExtend, boolean isFinal) {
    ProcessingEnvironment env = context.processingEnvironment();
    List properties = readProperties(context.properties(), env.getElementUtils(), env.getTypeUtils());

    Map types = convertPropertiesToTypes(context.properties());

    ClassName classNameClass = ClassName.get(context.packageName(), className);
    ClassName autoValueClass = ClassName.get(context.autoValueClass());

    TypeName superclasstype = ClassName.get(context.packageName(), classToExtend);
    TypeSpec typeAdapter = createTypeAdapter(context, classNameClass, autoValueClass, properties);

    TypeSpec.Builder subclass = TypeSpec.classBuilder(classNameClass)
        .superclass(superclasstype)
        .addType(typeAdapter)
        .addMethod(generateConstructor(types));

    if (isFinal) {
      subclass.addModifiers(FINAL);
    } else {
      subclass.addModifiers(ABSTRACT);
    }

    return JavaFile.builder(context.packageName(), subclass.build()).build().toString();
  }

  public List readProperties(Map properties, Elements elements, Types types) {
    List values = new LinkedList();
    for (Map.Entry entry : properties.entrySet()) {
      values.add(new Property(entry.getKey(), entry.getValue(), elements, types));
    }
    return values;
  }

  List createTypeConverterFields(List properties) {
    // TypeConverters could be expensive to create, so just use one per class
    Set usedTypeConverters = new HashSet<>();
    for (Property property : properties) {
      if (property.type instanceof TypeConverterFieldType) {
        usedTypeConverters.add(((TypeConverterFieldType) property.type).getTypeConverterClassName());
      }
    }
    List fieldSpecs = new ArrayList<>(usedTypeConverters.size());
    for (ClassName typeConverter : usedTypeConverters) {
      fieldSpecs.add(FieldSpec.builder(typeConverter, TypeUtils.getStaticFinalTypeConverterVariableName(typeConverter))
          .addModifiers(Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL)
          .initializer("new $T()", typeConverter)
          .build());
    }

    return fieldSpecs;
  }

  List createDynamicTypeConverterFields(Set usedTypeConverters) {
    List fieldSpecs = new ArrayList<>(usedTypeConverters.size());
    for (TypeName usedTypeConverter : usedTypeConverters) {
      final String variableName = TypeUtils.getTypeConverterVariableName(usedTypeConverter);
      fieldSpecs.add(
          FieldSpec
              .builder(ParameterizedTypeName.get(ClassName.get(TypeConverter.class), usedTypeConverter), variableName)
              .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
              .build()
      );
    }
    return fieldSpecs;
  }


  List createDynamicTypeConverterMethods(Set usedTypeConverters) {
    List methodSpecs = new ArrayList<>(usedTypeConverters.size());
    for (TypeName usedTypeConverter : usedTypeConverters) {
      final String variableName = TypeUtils.getTypeConverterVariableName(usedTypeConverter);
      methodSpecs.add(MethodSpec.methodBuilder(TypeUtils.getTypeConverterGetter(usedTypeConverter))
          .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
          .returns(ParameterizedTypeName.get(ClassName.get(TypeConverter.class), usedTypeConverter))
          .beginControlFlow("if ($L == null)", variableName)
          .addStatement("$L = $T.typeConverterFor($T.class)", variableName, LoganSquare.class, usedTypeConverter)
          .endControlFlow()
          .addStatement("return $L", variableName)
          .build()
      );
    }
    return methodSpecs;
  }

  ImmutableMap createMapperFields(List properties) {
    ImmutableMap.Builder fields = ImmutableMap.builder();

    Set mappers = new HashSet<>();
    for (Property property : properties) {
      mappers.addAll(property.type.getUsedJsonObjectMappers());
    }
    for (Type.ClassNameObjectMapper usedMapper : mappers) {
      fields.put(
          usedMapper.className,
          FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(JsonMapper.class), usedMapper.className),
              TypeUtils.getMapperVariableName(usedMapper.objectMapper))
              .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
              .build()
      );
    }

    return fields.build();
  }

  private List createDefaultValueFields(List properties,
      ProcessingEnvironment processingEnv) {
    List fields = new ArrayList<>(properties.size());
    boolean collectionsDefault = Boolean.parseBoolean(processingEnv.getOptions()
        .getOrDefault(COLLECTIONS_DEFAULT_TO_EMPTY, "false"));
    for (Property prop : properties) {
      FieldSpec fieldSpec = FieldSpec.builder(prop.typeName, "default" + upperCamelizeHumanName(prop), PRIVATE).build();
      CodeBlock defaultValue = getDefaultValue(prop, fieldSpec, collectionsDefault);
      if (defaultValue == null) {
        defaultValue = CodeBlock.of("null");
      }
      fields.add(fieldSpec.toBuilder()
          .initializer(defaultValue)
          .build());
    }
    return fields;
  }

  private String upperCamelizeHumanName(Property prop) {
    return CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, prop.humanName);
  }

  MethodSpec generateConstructor(Map properties) {
    List params = Lists.newArrayList();
    for (Map.Entry entry : properties.entrySet()) {
      params.add(ParameterSpec.builder(entry.getValue(), entry.getKey()).build());
    }

    MethodSpec.Builder builder = MethodSpec.constructorBuilder()
        .addParameters(params);

    StringBuilder superFormat = new StringBuilder("super(");
    for (int i = properties.size(); i > 0; i--) {
      superFormat.append("$N");
      if (i > 1) superFormat.append(", ");
    }
    superFormat.append(")");
    builder.addStatement(superFormat.toString(), properties.keySet().toArray());

    return builder.build();
  }

  /**
   * Converts the ExecutableElement properties to TypeName properties
   */
  Map convertPropertiesToTypes(Map properties) {
    Map types = new LinkedHashMap();
    for (Map.Entry entry : properties.entrySet()) {
      ExecutableElement el = entry.getValue();
      types.put(entry.getKey(), TypeName.get(el.getReturnType()));
    }
    return types;
  }

  public TypeSpec createTypeAdapter(Context context, ClassName className, ClassName autoValueClassName,
      List properties) {
    ClassName jsonMapperClass = ClassName.get(JsonMapper.class);
    TypeName autoValueTypeName = autoValueClassName;
    ParameterizedTypeName superClass = ParameterizedTypeName.get(jsonMapperClass, autoValueTypeName);

    ImmutableMap mappers = createMapperFields(properties);

    List defaultValues = createDefaultValueFields(properties, context.processingEnvironment());

    ParameterSpec loganSquareAutoParam = ParameterSpec.builder(JacksonAuto.class, "loganSquareAuto").build();
    ParameterSpec serializeNullObjectsParam = ParameterSpec.builder(boolean.class, "serializeNullObjects").build();
    ParameterSpec serializeNullCollectionElementsParam =
        ParameterSpec.builder(boolean.class, "serializeNullCollectionElements").build();
    MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
        .addParameter(loganSquareAutoParam)
        .addParameter(serializeNullObjectsParam)
        .addParameter(serializeNullCollectionElementsParam);


    // Initialize the serialize NULLs options
    FieldSpec serializeNullObjectsField = FieldSpec.builder(boolean.class, "serializeNullObjects")
        .addModifiers(PRIVATE, FINAL)
        .build();
    FieldSpec serializeNullCollectionElementsField = FieldSpec.builder(
        boolean.class, "serializeNullCollectionElements")
        .addModifiers(PRIVATE, FINAL)
        .build();

    constructor
        .addStatement("this.$N = $N", serializeNullObjectsField, serializeNullObjectsParam)
        .addStatement("this.$N = $N", serializeNullCollectionElementsField, serializeNullCollectionElementsParam);

    // Create the constructor with defaults for serialize NULLs
    MethodSpec.Builder constructorWithDefaults = MethodSpec.constructorBuilder()
        .addParameter(loganSquareAutoParam)
        .addModifiers(PUBLIC)
        .addStatement("this($N, false, false)", loganSquareAutoParam);

    // Initialize mappers
    for (Map.Entry entry : mappers.entrySet()) {
      TypeName typeName = entry.getKey();
      FieldSpec field = entry.getValue();
      if (field != null) {
        TypeName type = typeName.isPrimitive() ? typeName.box() :  typeName;
        constructor.addStatement("this.$N = $N.mapperFor($T.class)", field, loganSquareAutoParam, type);
      }
    }

    // Add type covnverter methods for static converter object.
    // TODO: Need to figure out when this applies.
    Set usedDynamicFieldTypeConverters = new HashSet<>();
    for (Property property : properties) {
      usedDynamicFieldTypeConverters.addAll(property.type.getUsedTypeConverters());
    }

    ClassName jsonObjectMapperName = className.nestedClass("JsonObjectMapper");

    TypeSpec.Builder classBuilder = TypeSpec.classBuilder(jsonObjectMapperName)
        .addModifiers(PUBLIC, STATIC, FINAL)
        .superclass(superClass)
        .addFields(createTypeConverterFields(properties))
        .addField(serializeNullObjectsField)
        .addField(serializeNullCollectionElementsField)
        //.addFields(createDynamicTypeConverterFields(usedDynamicFieldTypeConverters))
        .addFields(mappers.values())
        .addFields(defaultValues)
        .addMethod(constructor.build())
        .addMethod(constructorWithDefaults.build())
        //.addMethods(createDynamicTypeConverterMethods(usedDynamicFieldTypeConverters))
        .addMethods(createDefaultMethods(jsonObjectMapperName, properties))
        .addMethod(createSerializeMethod(autoValueTypeName, properties, serializeNullObjectsField,
            serializeNullCollectionElementsField))
        .addMethod(createParseMethod(className, autoValueTypeName, properties))
        .addMethod(createParseFieldMethod(autoValueTypeName));

    return classBuilder.build();
  }

  public List createDefaultMethods(ClassName gsonTypeAdapterName, List properties) {
    List methodSpecs = new ArrayList<>(properties.size());
    for (Property prop : properties) {
      ParameterSpec valueParam = ParameterSpec.builder(prop.typeName, "default" + upperCamelizeHumanName(prop)).build();

      methodSpecs.add(MethodSpec.methodBuilder("setDefault" + upperCamelizeHumanName(prop))
          .addModifiers(PUBLIC)
          .addParameter(valueParam)
          .returns(gsonTypeAdapterName)
          .addCode(CodeBlock.builder()
              .addStatement("this.default$L = $N", upperCamelizeHumanName(prop), valueParam)
              .addStatement("return this")
              .build())
          .build());
    }
    return methodSpecs;
  }

  public MethodSpec createSerializeMethod(TypeName autoValueClassName, List properties, FieldSpec serializeNullObjectsField,
      FieldSpec serializeNullCollectionElementsField)
  {
    ParameterSpec annotatedParam = ParameterSpec.builder(autoValueClassName, "object").build();
    ParameterSpec jsonGenerator = ParameterSpec.builder(JsonGenerator.class, "jsonGenerator").build();
    ParameterSpec writeStartAndEndParam = ParameterSpec.builder(boolean.class, "writeStartAndEnd").build();
    MethodSpec.Builder serializeMethod = MethodSpec.methodBuilder("serialize")
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(annotatedParam)
        .addParameter(jsonGenerator)
        .addParameter(writeStartAndEndParam)
        .addException(IOException.class);

    serializeMethod.beginControlFlow("if ($N)", writeStartAndEndParam);
    serializeMethod.addStatement("$N.writeStartObject()", jsonGenerator);
    serializeMethod.endControlFlow();

    serializeMethod.beginControlFlow("if ($N == null)", annotatedParam);
    serializeMethod.addStatement("$N.writeNull()", jsonGenerator);
    serializeMethod.addStatement("return");
    serializeMethod.endControlFlow();

    List processedFields = new ArrayList<>(properties.size());
    for (Property prop : properties) {
      prop.type.serialize(serializeMethod, 1, prop.jsonNames()[0], processedFields,
          "object." + prop.methodName + "()", true, true, serializeNullObjectsField.name,
          serializeNullCollectionElementsField.name);
    }

    serializeMethod.beginControlFlow("if ($N)", writeStartAndEndParam);
    serializeMethod.addStatement("$N.writeEndObject()", jsonGenerator);
    serializeMethod.endControlFlow();

    return serializeMethod.build();
  }

  public MethodSpec createParseMethod(ClassName className, TypeName autoValueClassName, List properties) {
    ParameterSpec jsonParser = ParameterSpec.builder(JsonParser.class, "jsonParser").build();
    MethodSpec.Builder parseMethod = MethodSpec.methodBuilder("parse")
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .returns(autoValueClassName)
        .addParameter(jsonParser)
        .addException(IOException.class);

    parseMethod.beginControlFlow("if ($N.getCurrentToken() == null)", jsonParser);
    parseMethod.addStatement("$N.nextToken()", jsonParser);
    parseMethod.endControlFlow();

    parseMethod.beginControlFlow("if ($N.getCurrentToken() != $T.START_OBJECT)", jsonParser, JsonToken.class);
    parseMethod.addStatement("$N.skipChildren()", jsonParser);
    parseMethod.addStatement("return null");
    parseMethod.endControlFlow();

    // add the properties
    Map fields = new LinkedHashMap(properties.size());
    for (Property prop : properties) {
      TypeName fieldType = prop.typeName;
      FieldSpec field = FieldSpec.builder(fieldType, prop.humanName).build();
      fields.put(prop, field);
      parseMethod.addStatement("$T $N = this.default$L", field.type, field.name, upperCamelizeHumanName(prop));
    }

    parseMethod.beginControlFlow("while ($N.nextToken() != $T.END_OBJECT)", jsonParser, JsonToken.class);

    FieldSpec name = FieldSpec.builder(String.class, "_name").build();
    parseMethod.addStatement("$T $N = $N.getCurrentName()", name.type, name, jsonParser);
    parseMethod.addStatement("$N.nextToken()", jsonParser);

    parseMethod.beginControlFlow("switch ($N)", name);
    for (Map.Entry entry : fields.entrySet()) {
      Property prop = entry.getKey();
      FieldSpec field = entry.getValue();

      boolean needToIndent = true;
      for (String jsonName : prop.jsonNames()) {
        if (needToIndent) {
          parseMethod.beginControlFlow("case $S:\n", jsonName);
          needToIndent = false;
        } else {
          parseMethod.addCode("case $S:\n", jsonName);
        }
      }
      prop.type.parse(parseMethod, 3, "$L = $L", new Object[]{prop.humanName});
      parseMethod.addStatement("break");
      parseMethod.endControlFlow();
    }

    parseMethod.endControlFlow(); // switch
    parseMethod.addStatement("$N.skipChildren()", jsonParser);

    parseMethod.endControlFlow(); // while

    StringBuilder format = new StringBuilder("return new ");
    format.append(className.simpleName().replaceAll("\\$", ""));
    if (autoValueClassName instanceof ParameterizedTypeName) {
      format.append("<>");
    }
    format.append("(");
    Iterator iterator = fields.values().iterator();
    while (iterator.hasNext()) {
      iterator.next();
      format.append("$N");
      if (iterator.hasNext()) format.append(", ");
    }
    format.append(")");
    parseMethod.addStatement(format.toString(), fields.values().toArray());

    return parseMethod.build();
  }

  public MethodSpec createParseFieldMethod(TypeName autoValueClassName) {
    return MethodSpec.methodBuilder("parseField")
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(ParameterSpec.builder(autoValueClassName, "instance").build())
        .addParameter(ParameterSpec.builder(String.class, "fieldName").build())
        .addParameter(ParameterSpec.builder(JsonParser.class, "jsonParser").build())
        .addException(IOException.class)
        .build();
  }

  /**
   * Returns a default value for initializing well-known types, or else {@code null}.
   */
  private CodeBlock getDefaultValue(Property prop, FieldSpec field, boolean collectionsDefaultToEmpty) {
    if (field.type.isPrimitive()) {
      String defaultValue = getDefaultPrimitiveValue(field.type);
      if (defaultValue != null) {
        return CodeBlock.of("$L", defaultValue);
      } else {
        return CodeBlock.of("$T.valueOf(null)", field.type, field, field.type.box());
      }
    }
    if (prop.nullable()) {
      return null;
    }
    TypeMirror type = prop.element.getReturnType();
    if (type.getKind() != TypeKind.DECLARED) {
      return null;
    }
    TypeElement typeElement = MoreTypes.asTypeElement(type);
    if (typeElement == null) {
      return null;
    }
    if (collectionsDefaultToEmpty) {
      try {
        Class clazz = Class.forName(typeElement.getQualifiedName()
            .toString());
        if (clazz.isAssignableFrom(List.class)) {
          return CodeBlock.of("$T.emptyList()", TypeName.get(Collections.class));
        } else if (clazz.isAssignableFrom(Map.class)) {
          return CodeBlock.of("$T.emptyMap()", TypeName.get(Collections.class));
        } else if (clazz.isAssignableFrom(Set.class)) {
          return CodeBlock.of("$T.emptySet()", TypeName.get(Collections.class));
        } else if (clazz.isAssignableFrom(ImmutableList.class)) {
          return CodeBlock.of("$T.of()", TypeName.get(ImmutableList.class));
        } else if (clazz.isAssignableFrom(ImmutableMap.class)) {
          return CodeBlock.of("$T.of()", TypeName.get(ImmutableMap.class));
        } else if (clazz.isAssignableFrom(ImmutableSet.class)) {
          return CodeBlock.of("$T.of()", TypeName.get(ImmutableSet.class));
        } else {
          return null;
        }
      } catch (ClassNotFoundException e) {
        return null;
      }
    } else {
      return null;
    }
  }

  /**
   * @param type
   * @return the default primitive value as a String.  Returns null if unable to determine default value
   */
  private String getDefaultPrimitiveValue(TypeName type) {
    String valueString = null;
    try {
      Class primitiveClass = Primitives.unwrap(Class.forName(type.box().toString()));
      if (primitiveClass != null) {
        Object defaultValue = Defaults.defaultValue(primitiveClass);
        if (defaultValue != null) {
          valueString = defaultValue.toString();
          if (!Strings.isNullOrEmpty(valueString)) {
            switch (type.toString()) {
              case "double":
                valueString = valueString + "d";
                break;
              case "float":
                valueString = valueString + "f";
                break;
              case "long":
                valueString = valueString + "L";
                break;
              case "char":
                valueString = "'" + valueString + "'";
                break;
            }
          }
        }
      }
    } catch (ClassNotFoundException ignored) {
      //Swallow and return null
    }

    return valueString;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy