From 9cc04ecb39166207a2835174b85ee209cc08aad0 Mon Sep 17 00:00:00 2001 From: emcmanus Date: Tue, 24 Jul 2018 13:21:32 -0700 Subject: [PATCH] Ensure that GwtSerialization support works in the presence of AutoValue extensions. RELNOTES=GwtSerialization support now works in the presence of AutoValue extensions. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=205880303 --- value/src/it/functional/pom.xml | 16 ++- .../google/auto/value/gwt/EmptyExtension.java | 135 ++++++++++++++++++ .../value/processor/GwtSerialization.java | 2 +- 3 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java diff --git a/value/src/it/functional/pom.xml b/value/src/it/functional/pom.xml index 0a98befbfc..c1a4bf9d6f 100644 --- a/value/src/it/functional/pom.xml +++ b/value/src/it/functional/pom.xml @@ -43,6 +43,16 @@ auto-value ${project.version} + + com.google.auto.service + auto-service + 1.0-rc4 + + + com.google.guava + guava + 23.5-jre + com.google.code.findbugs jsr305 @@ -60,12 +70,6 @@ 4.12 test - - com.google.guava - guava - 23.5-jre - test - com.google.guava guava-testlib diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java b/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java new file mode 100644 index 0000000000..f07c917108 --- /dev/null +++ b/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.auto.value.gwt; + +import static java.util.stream.Collectors.joining; + +import com.google.auto.service.AutoService; +import com.google.auto.value.extension.AutoValueExtension; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.escapevelocity.Template; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; + +/** + * An AutoValue extension that generates a subclass that does nothing useful. + */ +@AutoService(AutoValueExtension.class) +public class EmptyExtension extends AutoValueExtension { + // TODO(emcmanus): it is way too difficult to write a trivial extension. Problems we have here: + // (1) We have to generate a constructor that calls the superclass constructor, which means + // declaring the appropriate constructor parameters and then forwarding them to a super + // call. + // (2) We have to avoid generating variable names that are keywords (we append $ here + // to avoid that). + // (3) We have to concoct appropriate type parameter strings, for example + // final class AutoValue_Foo, V> extends $AutoValue_Foo. + // These problems show up with the template approach here, but also using JavaPoet as the + // Memoize extension does. + private static final ImmutableList TEMPLATE_LINES = + ImmutableList.of( + "package $package;", + "\n", + "#if ($isFinal) final #end class ${className}${formalTypes}" + + " extends ${classToExtend}${actualTypes} {\n", + " ${className}(", + " #foreach ($property in $properties.keySet())", + " $properties[$property].returnType ${property}$ #if ($foreach.hasNext) , #end", + " #end", + " ) {", + " super(", + " #foreach ($property in $properties.keySet())", + " ${property}$ #if ($foreach.hasNext) , #end", + " #end", + " );", + " }", + "}"); + + @Override + public boolean applicable(Context context) { + return true; + } + + @Override + public String generateClass( + Context context, String className, String classToExtend, boolean isFinal) { + String templateString = Joiner.on('\n').join(TEMPLATE_LINES); + StringReader templateReader = new StringReader(templateString); + Template template; + try { + template = Template.parseFrom(templateReader); + } catch (IOException e) { + throw new RuntimeException(e); + } + TypeElement autoValueClass = context.autoValueClass(); + ImmutableMap vars = + ImmutableMap.builder() + .put("package", context.packageName()) + .put("className", className) + .put("classToExtend", classToExtend) + .put("isFinal", isFinal) + .put("properties", context.properties()) + .put("formalTypes", formalTypeParametersString(autoValueClass)) + .put("actualTypes", actualTypeParametersString(autoValueClass)) + .build(); + return template.evaluate(vars); + } + + private static String actualTypeParametersString(TypeElement type) { + List typeParameters = type.getTypeParameters(); + if (typeParameters.isEmpty()) { + return ""; + } + return typeParameters + .stream() + .map(e -> e.getSimpleName().toString()) + .collect(joining(", ", "<", ">")); + } + + private static String formalTypeParametersString(TypeElement type) { + List typeParameters = type.getTypeParameters(); + if (typeParameters.isEmpty()) { + return ""; + } + StringBuilder sb = new StringBuilder("<"); + String sep = ""; + for (TypeParameterElement typeParameter : typeParameters) { + sb.append(sep); + sep = ", "; + appendTypeParameterWithBounds(typeParameter, sb); + } + return sb.append(">").toString(); + } + + private static void appendTypeParameterWithBounds( + TypeParameterElement typeParameter, StringBuilder sb) { + sb.append(typeParameter.getSimpleName()); + String sep = " extends "; + for (TypeMirror bound : typeParameter.getBounds()) { + if (!bound.toString().equals("java.lang.Object")) { + sb.append(sep); + sep = " & "; + sb.append(bound); + } + } + } +} diff --git a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java index 1d64162dde..9bcf5614cc 100644 --- a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java +++ b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java @@ -83,7 +83,7 @@ void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars) { if (shouldWriteGwtSerializer()) { GwtTemplateVars vars = new GwtTemplateVars(); vars.pkg = autoVars.pkg; - vars.subclass = autoVars.subclass; + vars.subclass = autoVars.finalSubclass; vars.formalTypes = autoVars.formalTypes; vars.actualTypes = autoVars.actualTypes; vars.useBuilder = !autoVars.builderTypeName.isEmpty();