blob: dde449bb5fe9905a4dac66f76f19f1bede7a4445 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* 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.processor;
import static com.google.common.collect.Sets.difference;
import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import java.util.Optional;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
class BuilderMethodClassifierForAutoValue extends BuilderMethodClassifier<ExecutableElement> {
private final ErrorReporter errorReporter;
private final ImmutableBiMap<ExecutableElement, String> getterToPropertyName;
private final ImmutableMap<String, ExecutableElement> getterNameToGetter;
private final TypeMirror builtType;
private BuilderMethodClassifierForAutoValue(
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
TypeMirror builtType,
TypeElement builderType,
ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
super(errorReporter, processingEnv, builtType, builderType, rewrittenPropertyTypes);
this.errorReporter = errorReporter;
this.getterToPropertyName = getterToPropertyName;
this.getterNameToGetter =
Maps.uniqueIndex(getterToPropertyName.keySet(), m -> m.getSimpleName().toString());
this.builtType = builtType;
}
/**
* Classifies the given methods from a builder type and its ancestors.
*
* @param methods the abstract methods in {@code builderType} and its ancestors.
* @param errorReporter where to report errors.
* @param processingEnv the {@link ProcessingEnvironment} for annotation processing.
* @param autoValueClass the {@code AutoValue} class containing the builder.
* @param builderType the builder class or interface within {@code autoValueClass}.
* @param getterToPropertyName a map from getter methods to the properties they get.
* @param rewrittenPropertyTypes a map from property names to types. The types here use type
* parameters from the builder class (if any) rather than from the {@code AutoValue} class,
* even though the getter methods are in the latter.
* @param autoValueHasToBuilder true if the containing {@code @AutoValue} class has a {@code
* toBuilder()} method.
* @return an {@code Optional} that contains the results of the classification if it was
* successful or nothing if it was not.
*/
static Optional<BuilderMethodClassifier<ExecutableElement>> classify(
Iterable<ExecutableElement> methods,
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
TypeElement autoValueClass,
TypeElement builderType,
ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
ImmutableMap<String, TypeMirror> rewrittenPropertyTypes,
boolean autoValueHasToBuilder) {
BuilderMethodClassifier<ExecutableElement> classifier =
new BuilderMethodClassifierForAutoValue(
errorReporter,
processingEnv,
autoValueClass.asType(),
builderType,
getterToPropertyName,
rewrittenPropertyTypes);
if (classifier.classifyMethods(methods, autoValueHasToBuilder)) {
return Optional.of(classifier);
} else {
return Optional.empty();
}
}
@Override
TypeMirror originalPropertyType(ExecutableElement propertyElement) {
return propertyElement.getReturnType();
}
@Override
String propertyString(ExecutableElement propertyElement) {
TypeElement type = MoreElements.asType(propertyElement.getEnclosingElement());
return "property method "
+ type.getQualifiedName()
+ "."
+ propertyElement.getSimpleName()
+ "()";
}
@Override
ImmutableBiMap<String, ExecutableElement> propertyElements() {
return getterToPropertyName.inverse();
}
@Override
Optional<String> propertyForBuilderGetter(ExecutableElement method) {
String methodName = method.getSimpleName().toString();
return Optional.ofNullable(getterNameToGetter.get(methodName)).map(getterToPropertyName::get);
}
@Override
void checkForFailedJavaBean(ExecutableElement rejectedSetter) {
ImmutableSet<ExecutableElement> allGetters = getterToPropertyName.keySet();
ImmutableSet<ExecutableElement> prefixedGetters =
AutoValueProcessor.prefixedGettersIn(allGetters);
if (prefixedGetters.size() < allGetters.size()
&& prefixedGetters.size() >= allGetters.size() / 2) {
errorReporter.reportNote(
rejectedSetter,
"This might be because you are using the getFoo() convention"
+ " for some but not all methods. These methods don't follow the convention: %s",
difference(allGetters, prefixedGetters));
}
}
@Override
String autoWhat() {
return "AutoValue";
}
@Override
String getterMustMatch() {
return "a property method of " + builtType;
}
@Override
String fooBuilderMustMatch() {
return "foo() or getFoo()";
}
}