• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.gwt;
17 
18 import static java.util.stream.Collectors.joining;
19 
20 import com.google.auto.service.AutoService;
21 import com.google.auto.value.extension.AutoValueExtension;
22 import com.google.common.base.Joiner;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.escapevelocity.Template;
26 import java.io.IOException;
27 import java.io.StringReader;
28 import java.util.List;
29 import javax.lang.model.element.TypeElement;
30 import javax.lang.model.element.TypeParameterElement;
31 import javax.lang.model.type.TypeMirror;
32 
33 /**
34  * An AutoValue extension that generates a subclass that does nothing useful.
35  */
36 @AutoService(AutoValueExtension.class)
37 public class EmptyExtension extends AutoValueExtension {
38   // TODO(emcmanus): it is way too difficult to write a trivial extension. Problems we have here:
39   //   (1) We have to generate a constructor that calls the superclass constructor, which means
40   //       declaring the appropriate constructor parameters and then forwarding them to a super
41   //       call.
42   //   (2) We have to avoid generating variable names that are keywords (we append $ here
43   //       to avoid that).
44   //   (3) We have to concoct appropriate type parameter strings, for example
45   //       final class AutoValue_Foo<K extends Comparable<K>, V> extends $AutoValue_Foo<K, V>.
46   //   These problems show up with the template approach here, but also using JavaPoet as the
47   //   Memoize extension does.
48   private static final ImmutableList<String> TEMPLATE_LINES =
49       ImmutableList.of(
50           "package $package;",
51           "\n",
52           "#if ($isFinal) final #end class ${className}${formalTypes}"
53               + " extends ${classToExtend}${actualTypes} {\n",
54           "  ${className}(",
55           "    #foreach ($property in $propertyTypes.keySet())",
56           "    $propertyTypes[$property] ${property}$ #if ($foreach.hasNext) , #end",
57           "    #end",
58           "  ) {",
59           "    super(",
60           "      #foreach ($property in $propertyTypes.keySet())",
61           "      ${property}$ #if ($foreach.hasNext) , #end",
62           "      #end",
63           "    );",
64           "  }",
65           "}");
66 
67   @Override
applicable(Context context)68   public boolean applicable(Context context) {
69     return true;
70   }
71 
72   @Override
generateClass( Context context, String className, String classToExtend, boolean isFinal)73   public String generateClass(
74       Context context, String className, String classToExtend, boolean isFinal) {
75     String templateString = Joiner.on('\n').join(TEMPLATE_LINES);
76     StringReader templateReader = new StringReader(templateString);
77     Template template;
78     try {
79       template = Template.parseFrom(templateReader);
80     } catch (IOException e) {
81       throw new RuntimeException(e);
82     }
83     TypeElement autoValueClass = context.autoValueClass();
84     ImmutableMap<String, Object> vars =
85         ImmutableMap.<String, Object>builder()
86             .put("package", context.packageName())
87             .put("className", className)
88             .put("classToExtend", classToExtend)
89             .put("isFinal", isFinal)
90             .put("propertyTypes", context.propertyTypes())
91             .put("formalTypes", formalTypeParametersString(autoValueClass))
92             .put("actualTypes", actualTypeParametersString(autoValueClass))
93             .build();
94     return template.evaluate(vars);
95   }
96 
actualTypeParametersString(TypeElement type)97   private static String actualTypeParametersString(TypeElement type) {
98     List<? extends TypeParameterElement> typeParameters = type.getTypeParameters();
99     if (typeParameters.isEmpty()) {
100       return "";
101     }
102     return typeParameters.stream()
103         .map(e -> e.getSimpleName().toString())
104         .collect(joining(", ", "<", ">"));
105   }
106 
formalTypeParametersString(TypeElement type)107   private static String formalTypeParametersString(TypeElement type) {
108     List<? extends TypeParameterElement> typeParameters = type.getTypeParameters();
109     if (typeParameters.isEmpty()) {
110       return "";
111     }
112     StringBuilder sb = new StringBuilder("<");
113     String sep = "";
114     for (TypeParameterElement typeParameter : typeParameters) {
115       sb.append(sep);
116       sep = ", ";
117       appendTypeParameterWithBounds(typeParameter, sb);
118     }
119     return sb.append(">").toString();
120   }
121 
appendTypeParameterWithBounds( TypeParameterElement typeParameter, StringBuilder sb)122   private static void appendTypeParameterWithBounds(
123       TypeParameterElement typeParameter, StringBuilder sb) {
124     sb.append(typeParameter.getSimpleName());
125     String sep = " extends ";
126     for (TypeMirror bound : typeParameter.getBounds()) {
127       if (!bound.toString().equals("java.lang.Object")) {
128         sb.append(sep);
129         sep = " & ";
130         sb.append(bound);
131       }
132     }
133   }
134 }
135