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