diff options
author | Éamonn McManus <emcmanus@google.com> | 2021-04-13 08:46:25 -0700 |
---|---|---|
committer | Google Java Core Libraries <java-libraries-firehose+copybara@google.com> | 2021-04-13 08:46:54 -0700 |
commit | 6fa21a43a040c11a68724fc39d3306f0d41e3e0c (patch) | |
tree | 26c43e074ac1d749615a7bdd26aa89690a3de0b4 /value/src/main/java/com/google/auto | |
parent | d363f9101bd7390cdd1e28327fa9738dc3fab4bd (diff) | |
download | auto-6fa21a43a040c11a68724fc39d3306f0d41e3e0c.tar.gz |
Add support for builder-getters to AutoBuilder.
A builder-getter is a method in the builder interface that lets you see what value has been set for a property of the builder. Like `@AutoValue.Builder`, the getter for a property of type `T` can return either `T` or `Optional<T>`. Trying to get a property that has not been set will provoke an exception for the former or return `Optional.empty()` for the latter.
With `@AutoValue.Builder`, a builder-getter method corresponds to a method with the same signature in the `@AutoValue` class. Here, a builder-getter method corresponds to a parameter with the same name and type, except that the builder-getter for parameter `foo` can be either `foo()` or `getFoo()`.
RELNOTES=n/a
PiperOrigin-RevId: 368222631
Diffstat (limited to 'value/src/main/java/com/google/auto')
6 files changed, 81 insertions, 45 deletions
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java index b2310875..a04c30c6 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java @@ -33,11 +33,13 @@ import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.common.Visibility; import com.google.auto.service.AutoService; +import com.google.auto.value.processor.BuilderSpec.PropertyGetter; import com.google.auto.value.processor.MissingTypes.MissingTypeException; import com.google.common.base.Ascii; import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import java.lang.reflect.Field; import java.util.List; import java.util.Map; @@ -121,16 +123,19 @@ public class AutoBuilderProcessor extends AutoValueishProcessor { BuilderSpec builderSpec = new BuilderSpec(ofClass, processingEnv, errorReporter()); BuilderSpec.Builder builder = builderSpec.new Builder(autoBuilderType); TypeMirror builtType = builtType(executable); - Optional<BuilderMethodClassifier<VariableElement>> classifier = + Optional<BuilderMethodClassifier<VariableElement>> maybeClassifier = BuilderMethodClassifierForAutoBuilder.classify( methods, errorReporter(), processingEnv, executable, builtType, autoBuilderType); - if (!classifier.isPresent()) { + if (!maybeClassifier.isPresent()) { // We've already output one or more error messages. return; } + BuilderMethodClassifier<VariableElement> classifier = maybeClassifier.get(); + Map<String, String> propertyToGetterName = + Maps.transformValues(classifier.builderGetters(), PropertyGetter::getName); AutoBuilderTemplateVars vars = new AutoBuilderTemplateVars(); - vars.props = propertySet(executable); - builder.defineVars(vars, classifier.get()); + vars.props = propertySet(executable, propertyToGetterName); + builder.defineVars(vars, classifier); vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION); String generatedClassName = generatedClassName(autoBuilderType, "AutoBuilder_"); vars.builderName = TypeSimplifier.simpleNameOf(generatedClassName); @@ -145,7 +150,8 @@ public class AutoBuilderProcessor extends AutoValueishProcessor { writeSourceFile(generatedClassName, text, autoBuilderType); } - private ImmutableSet<Property> propertySet(ExecutableElement executable) { + private ImmutableSet<Property> propertySet( + ExecutableElement executable, Map<String, String> propertyToGetterName) { // Fix any parameter names that are reserved words in Java. Java source code can't have // such parameter names, but Kotlin code might, for example. Map<VariableElement, String> identifiers = @@ -153,14 +159,18 @@ public class AutoBuilderProcessor extends AutoValueishProcessor { .collect(toMap(v -> v, v -> v.getSimpleName().toString())); fixReservedIdentifiers(identifiers); return executable.getParameters().stream() - .map(v -> newProperty(v, identifiers.get(v))) + .map( + v -> + newProperty( + v, identifiers.get(v), propertyToGetterName.get(v.getSimpleName().toString()))) .collect(toImmutableSet()); } - private Property newProperty(VariableElement var, String identifier) { + private Property newProperty(VariableElement var, String identifier, String getterName) { String name = var.getSimpleName().toString(); TypeMirror type = var.asType(); - return new Property(name, identifier, TypeEncoder.encode(type), type, Optional.empty()); + return new Property( + name, identifier, TypeEncoder.encode(type), type, Optional.empty(), getterName); } private ExecutableElement findExecutable( diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java index 20989f05..a494b9a6 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java @@ -21,6 +21,7 @@ import static com.google.auto.common.MoreElements.getPackage; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_PACKAGE_NAME; import static com.google.auto.value.processor.ClassNames.COPY_ANNOTATIONS_NAME; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Sets.union; import static java.util.stream.Collectors.joining; @@ -155,19 +156,22 @@ abstract class AutoValueishProcessor extends AbstractProcessor { private final TypeMirror typeMirror; private final Optional<String> nullableAnnotation; private final Optionalish optional; + private final String getter; Property( String name, String identifier, String type, TypeMirror typeMirror, - Optional<String> nullableAnnotation) { + Optional<String> nullableAnnotation, + String getter) { this.name = name; this.identifier = identifier; this.type = type; this.typeMirror = typeMirror; this.nullableAnnotation = nullableAnnotation; this.optional = Optionalish.createIfOptional(typeMirror); + this.getter = getter; } /** @@ -227,6 +231,16 @@ abstract class AutoValueishProcessor extends AbstractProcessor { public boolean isNullable() { return nullableAnnotation.isPresent(); } + + /** + * Returns the name of the getter method for this property as defined by the {@code @AutoValue} + * or {@code @AutoBuilder} class. For property {@code foo}, this will be {@code foo} or {@code + * getFoo} or {@code isFoo}. For AutoValue, this will also be the name of a getter method in a + * builder; in the case of AutoBuilder it will only be that and may be null. + */ + public String getGetter() { + return getter; + } } /** A {@link Property} that corresponds to an abstract getter method in the source. */ @@ -248,21 +262,14 @@ abstract class AutoValueishProcessor extends AbstractProcessor { identifier, type, method.getReturnType(), - nullableAnnotation); + nullableAnnotation, + method.getSimpleName().toString()); this.method = method; this.fieldAnnotations = fieldAnnotations; this.methodAnnotations = methodAnnotations; } /** - * Returns the name of the getter method for this property as defined by the {@code @AutoValue} - * class. For property {@code foo}, this will be {@code foo} or {@code getFoo} or {@code isFoo}. - */ - public String getGetter() { - return method.getSimpleName().toString(); - } - - /** * Returns the annotations (in string form) that should be applied to the property's field * declaration. */ @@ -604,21 +611,19 @@ abstract class AutoValueishProcessor extends AbstractProcessor { * includes {@code isFoo} methods if they return {@code boolean}. This corresponds to JavaBeans * conventions. */ - static ImmutableSet<ExecutableElement> prefixedGettersIn(Iterable<ExecutableElement> methods) { - ImmutableSet.Builder<ExecutableElement> getters = ImmutableSet.builder(); - for (ExecutableElement method : methods) { - String name = method.getSimpleName().toString(); - // Note that getfoo() (without a capital) is still a getter. - boolean get = name.startsWith("get") && !name.equals("get"); - boolean is = - name.startsWith("is") - && !name.equals("is") - && method.getReturnType().getKind() == TypeKind.BOOLEAN; - if (get || is) { - getters.add(method); - } - } - return getters.build(); + static ImmutableSet<ExecutableElement> prefixedGettersIn(Collection<ExecutableElement> methods) { + return methods.stream() + .filter(AutoValueishProcessor::isPrefixedGetter) + .collect(toImmutableSet()); + } + + static boolean isPrefixedGetter(ExecutableElement method) { + String name = method.getSimpleName().toString(); + // Note that getfoo() (without a capital) is still a getter. + return (name.startsWith("get") && !name.equals("get")) + || (name.startsWith("is") + && !name.equals("is") + && method.getReturnType().getKind() == TypeKind.BOOLEAN); } /** diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java index a40467dc..0bcaa682 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java @@ -234,15 +234,15 @@ abstract class BuilderMethodClassifier<E extends Element> { * ImmutableList<String> foos()} or {@code getFoos()}. */ private void classifyMethodNoArgs(ExecutableElement method) { - String methodName = method.getSimpleName().toString(); - TypeMirror returnType = builderMethodReturnType(method); - - Optional<String> getterProperty = propertyForBuilderGetter(methodName); + Optional<String> getterProperty = propertyForBuilderGetter(method); if (getterProperty.isPresent()) { classifyGetter(method, getterProperty.get()); return; } + String methodName = method.getSimpleName().toString(); + TypeMirror returnType = builderMethodReturnType(method); + if (methodName.endsWith("Builder")) { String property = methodName.substring(0, methodName.length() - "Builder".length()); if (rewrittenPropertyTypes.containsKey(property)) { @@ -683,12 +683,12 @@ abstract class BuilderMethodClassifier<E extends Element> { abstract String propertyString(E propertyElement); /** - * Returns the name of the property that a no-arg builder method of the given name queries, if + * Returns the name of the property that the given no-arg builder method queries, if * any. For example, if your {@code @AutoValue} class has a method {@code abstract String * getBar()} then an abstract method in its builder with the same signature will query the {@code * bar} property. */ - abstract Optional<String> propertyForBuilderGetter(String methodName); + abstract Optional<String> propertyForBuilderGetter(ExecutableElement method); /** * Checks for failed JavaBean usage when a method that looks like a setter doesn't actually match diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java index 16187de6..94b258f1 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java @@ -189,11 +189,24 @@ class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<Vari } @Override - Optional<String> propertyForBuilderGetter(String methodName) { - // TODO: handle getFoo -> foo - return paramToPropertyName.containsValue(methodName) - ? Optional.of(methodName) - : Optional.empty(); + Optional<String> propertyForBuilderGetter(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + if (paramToPropertyName.containsValue(methodName)) { + return Optional.of(methodName); + } + if (AutoValueishProcessor.isPrefixedGetter(method)) { + int prefixLength = methodName.startsWith("get") ? 3 : 2; // "get" or "is" + String unprefixed = methodName.substring(prefixLength); + String propertyName = PropertyNames.decapitalizeLikeJavaBeans(unprefixed); + if (paramToPropertyName.containsValue(propertyName)) { + return Optional.of(propertyName); + } + propertyName = PropertyNames.decapitalizeNormally(unprefixed); + if (paramToPropertyName.containsValue(propertyName)) { + return Optional.of(propertyName); + } + } + return Optional.empty(); } @Override diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java index 39fa157d..dde449bb 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java @@ -111,7 +111,8 @@ class BuilderMethodClassifierForAutoValue extends BuilderMethodClassifier<Execut } @Override - Optional<String> propertyForBuilderGetter(String methodName) { + Optional<String> propertyForBuilderGetter(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); return Optional.ofNullable(getterNameToGetter.get(methodName)).map(getterToPropertyName::get); } diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java index 886b4d85..017a5ff5 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java @@ -354,6 +354,7 @@ class BuilderSpec { * five) then {@code Optional<T>} can be the corresponding boxed type. */ public static class PropertyGetter { + private final String name; private final String access; private final String type; private final Optionalish optional; @@ -371,11 +372,17 @@ class BuilderSpec { * {@code Optional<T>} rather than {@code T}, as explained above. */ PropertyGetter(ExecutableElement method, String type, Optionalish optional) { + this.name = method.getSimpleName().toString(); this.access = SimpleMethod.access(method); this.type = type; this.optional = optional; } + // Not accessed from templates so doesn't have to be public. + String getName() { + return name; + } + public String getAccess() { return access; } |