aboutsummaryrefslogtreecommitdiff
path: root/value/src/main/java/com/google/auto
diff options
context:
space:
mode:
authorÉamonn McManus <emcmanus@google.com>2021-04-13 08:46:25 -0700
committerGoogle Java Core Libraries <java-libraries-firehose+copybara@google.com>2021-04-13 08:46:54 -0700
commit6fa21a43a040c11a68724fc39d3306f0d41e3e0c (patch)
tree26c43e074ac1d749615a7bdd26aa89690a3de0b4 /value/src/main/java/com/google/auto
parentd363f9101bd7390cdd1e28327fa9738dc3fab4bd (diff)
downloadauto-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')
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java26
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java55
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java12
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java23
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java3
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderSpec.java7
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;
}