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

com.enigmabridge.ebuilder.processor.GwtSupport Maven / Gradle / Ivy

package com.enigmabridge.ebuilder.processor;

import static com.enigmabridge.ebuilder.processor.util.ModelUtils.findAnnotationMirror;
import static com.enigmabridge.ebuilder.processor.util.ModelUtils.findProperty;

import com.enigmabridge.ebuilder.processor.util.Excerpt;
import com.enigmabridge.ebuilder.processor.util.Excerpts;
import com.enigmabridge.ebuilder.processor.util.SourceBuilder;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Optional;

import com.enigmabridge.ebuilder.processor.util.QualifiedName;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.TypeElement;

class GwtSupport {

  private static final QualifiedName CUSTOM_FIELD_SERIALIZER =
      QualifiedName.of("com.google.gwt.user.client.rpc", "CustomFieldSerializer");
  private static final QualifiedName SERIALIZATION_EXCEPTION =
      QualifiedName.of("com.google.gwt.user.client.rpc", "SerializationException");
  private static final QualifiedName SERIALIZATION_STREAM_READER =
      QualifiedName.of("com.google.gwt.user.client.rpc", "SerializationStreamReader");
  private static final QualifiedName SERIALIZATION_STREAM_WRITER =
      QualifiedName.of("com.google.gwt.user.client.rpc", "SerializationStreamWriter");

  public static Metadata.Builder gwtMetadata(TypeElement type, Metadata metadata) {
    Metadata.Builder extraMetadata = new Metadata.Builder();
    Optional annotation = findAnnotationMirror(type, GwtCompatible.class);
    if (annotation.isPresent()) {
      extraMetadata.addGeneratedBuilderAnnotations(Excerpts.add("@%s%n", GwtCompatible.class));
      Optional serializable = findProperty(annotation.get(), "serializable");
      if (serializable.isPresent() && serializable.get().getValue().equals(Boolean.TRUE)) {
        // Due to a bug in GWT's handling of nested types, we have to declare Value as package
        // scoped so Value_CustomFieldSerializer can access it.
        extraMetadata.setValueTypeVisibility(Metadata.Visibility.PACKAGE);
        extraMetadata.addValueTypeAnnotations(Excerpts.add(
            "@%s(serializable = true)%n", GwtCompatible.class));
        extraMetadata.addNestedClasses(new CustomValueSerializer());
        extraMetadata.addNestedClasses(new GwtWhitelist());
        QualifiedName builderName = metadata.getGeneratedABuilder().getQualifiedName();
        extraMetadata.addVisibleNestedTypes(
            builderName.nestedType("Value_CustomFieldSerializer"),
            builderName.nestedType("GwtWhitelist"));
      }
    }
    return extraMetadata;
  }

  private static final class CustomValueSerializer implements Function {
    @Override
    public Excerpt apply(final Metadata metadata) {
      return new CustomValueSerializerExcerpt(metadata);
    }
  }

  private static final class CustomValueSerializerExcerpt extends Excerpt {
    private final Metadata metadata;

    private CustomValueSerializerExcerpt(Metadata metadata) {
      this.metadata = metadata;
    }

    @Override
    public void addTo(SourceBuilder code) {
      code.addLine("")
          .addLine("@%s", GwtCompatible.class);
      if (metadata.getType().isParameterized()) {
        code.addLine("@%s(\"unchecked\")", SuppressWarnings.class);
      }
      code.addLine("public static class Value_CustomFieldSerializer")
          .addLine("    extends %s<%s> {", CUSTOM_FIELD_SERIALIZER, metadata.getValueType())
          .addLine("")
          .addLine("  @%s", Override.class)
          .addLine("  public void deserializeInstance(%s reader, %s instance) { }",
              SERIALIZATION_STREAM_READER, metadata.getValueType())
          .addLine("")
          .addLine("  @%s", Override.class)
          .addLine("  public boolean hasCustomInstantiateInstance() {")
          .addLine("    return true;")
          .addLine("  }")
          .addLine("")
          .addLine("  @%s", Override.class)
          .addLine("  public %s instantiateInstance(%s reader)",
              metadata.getValueType(), SERIALIZATION_STREAM_READER)
          .addLine("      throws %s {", SERIALIZATION_EXCEPTION)
//          .addLine("    %1$s builder = new %1$s();", metadata.getBuilder());
          .addLine("    %1$s builder = %s;", metadata.getBuilder(), "getNewBuilder()");
      for (Metadata.Property property : metadata.getProperties()) {
        if (property.getType().getKind().isPrimitive()) {
          code.addLine("      %s %s = reader.read%s();",
              property.getType(), property.getName(), withInitialCapital(property.getType()));
          property.getCodeGenerator()
              .addSetFromResult(code, "builder", property.getName());
        } else if (String.class.getName().equals(property.getType().toString())) {
          code.addLine("      %s %s = reader.readString();",
              property.getType(), property.getName());
          property.getCodeGenerator()
              .addSetFromResult(code, "builder", property.getName());
        } else {
          code.addLine("    try {");
          if (!property.isFullyCheckedCast()) {
            code.addLine("      @SuppressWarnings(\"unchecked\")");
          }
          code.addLine("      %1$s %2$s = (%1$s) reader.readObject();",
                  property.getType(), property.getName());
          property.getCodeGenerator()
              .addSetFromResult(code, "builder", property.getName());
          code.addLine("    } catch (%s e) {", ClassCastException.class)
              .addLine("      throw new %s(", SERIALIZATION_EXCEPTION)
              .addLine("          \"Wrong type for property '%s'\", e);", property.getName())
              .addLine("    }");
        }
      }
      code.addLine("    return (%s) builder.build();", metadata.getValueType())
          .addLine("  }")
          .addLine("")
          .addLine("  @%s", Override.class)
          .addLine("  public void serializeInstance(%s writer, %s instance)",
              SERIALIZATION_STREAM_WRITER, metadata.getValueType())
          .addLine("      throws %s {", SERIALIZATION_EXCEPTION);
      for (Metadata.Property property : metadata.getProperties()) {
        if (property.getType().getKind().isPrimitive()) {
          code.add("    writer.write%s(",
              withInitialCapital(property.getType()), property.getName());
        } else if (String.class.getName().equals(property.getType().toString())) {
          code.add("    writer.writeString(", property.getName());
        } else {
          code.add("    writer.writeObject(", property.getName());
        }
        property.getCodeGenerator().addReadValueFragment(code, "instance." + property.getName());
        code.add(");\n");
      }
      code.addLine("  }")
          .addLine("")
          .addLine("  private static final Value_CustomFieldSerializer INSTANCE ="
              + " new Value_CustomFieldSerializer();")
          .addLine("")
          .addLine("  public static void deserialize(%s reader, %s instance) {",
              SERIALIZATION_STREAM_READER, metadata.getValueType())
          .addLine("    INSTANCE.deserializeInstance(reader, instance);")
          .addLine("  }")
          .addLine("")
          .addLine("  public static %s instantiate(%s reader)",
              metadata.getValueType(), SERIALIZATION_STREAM_READER)
          .addLine("      throws %s {", SERIALIZATION_EXCEPTION)
          .addLine("    return INSTANCE.instantiateInstance(reader);")
          .addLine("  }")
          .addLine("")
          .addLine("  public static void serialize(%s writer, %s instance)",
              SERIALIZATION_STREAM_WRITER, metadata.getValueType())
          .addLine("      throws %s {", SERIALIZATION_EXCEPTION)
          .addLine("    INSTANCE.serializeInstance(writer, instance);")
          .addLine("  }")
          .addLine("}");
    }

    @Override
    protected void addFields(FieldReceiver fields) {
      fields.add("metadata", metadata);
    }
  }

  private static final class GwtWhitelist implements Function {
    @Override
    public Excerpt apply(final Metadata metadata) {
      return new GwtWhitelistExcerpt(metadata);
    }
  }

  private static final class GwtWhitelistExcerpt extends Excerpt {
    private final Metadata metadata;

    private GwtWhitelistExcerpt(Metadata metadata) {
      this.metadata = metadata;
    }

    @Override
    public void addTo(SourceBuilder code) {
      code.addLine("")
          .addLine("/** This class exists solely to ensure GWT whitelists all required types. */")
          .addLine("@%s(serializable = true)", GwtCompatible.class)
          .addLine("static final class GwtWhitelist%s %s %s {",
              metadata.getType().declarationParameters(),
              metadata.isInterfaceType() ? "implements " : "extends ",
              metadata.getType())
          .addLine("");
      for (Metadata.Property property : metadata.getProperties()) {
        code.addLine("  %s %s;", property.getType(), property.getName());
      }
      code.addLine("")
          .addLine("  private GwtWhitelist() {")
          .addLine("    throw new %s();", UnsupportedOperationException.class)
          .addLine("   }");
      for (Metadata.Property property : metadata.getProperties()) {
        code.addLine("")
            .addLine("  @%s", Override.class)
            .addLine("  public %s %s() {", property.getType(), property.getGetterName())
            .addLine("    throw new %s();", UnsupportedOperationException.class)
            .addLine("  }");
      }
      code.addLine("}");
    }

    @Override
    protected void addFields(FieldReceiver fields) {
      fields.add("metadata", metadata);
    }
  }

  private static String withInitialCapital(Object obj) {
    String s = obj.toString();
    return s.substring(0, 1).toUpperCase() + s.substring(1);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy