use of com.google.auto.value.processor.BuilderSpec.Copier in project auto by google.
the class BuilderMethodClassifier method getConvertingSetterFunction.
/**
* Returns an {@code Optional} describing how to convert a value from the setter's parameter type
* to the getter's return type using one of the given methods, or {@code Optional.empty()} if the
* conversion isn't possible. An error will have been reported in the latter case.
*/
private Optional<Copier> getConvertingSetterFunction(ImmutableList<ExecutableElement> copyOfMethods, E propertyElement, ExecutableElement setter, TypeMirror parameterType) {
String property = propertyElements().inverse().get(propertyElement);
DeclaredType targetType = MoreTypes.asDeclared(rewrittenPropertyTypes.get(property));
for (ExecutableElement copyOfMethod : copyOfMethods) {
Optional<Copier> function = getConvertingSetterFunction(copyOfMethod, targetType, parameterType);
if (function.isPresent()) {
return function;
}
}
String targetTypeSimpleName = targetType.asElement().getSimpleName().toString();
errorReporter.reportError(setter, "[%sGetVsSetOrConvert] Parameter type %s of setter method should be %s to match %s, or it" + " should be a type that can be passed to %s.%s to produce %s", autoWhat(), parameterType, targetType, propertyString(propertyElement), targetTypeSimpleName, copyOfMethods.get(0).getSimpleName(), targetType);
return Optional.empty();
}
use of com.google.auto.value.processor.BuilderSpec.Copier in project auto by google.
the class BuilderMethodClassifier method classifyMethodOneArg.
/**
* Classifies a method given that it has one argument. A method with one argument can be:
*
* <ul>
* <li>a setter, meaning that it looks like {@code foo(T)} or {@code setFoo(T)}, where the
* {@code AutoValue} class has a property called {@code foo} of type {@code T};
* <li>a property builder with one argument, meaning it looks like {@code
* ImmutableSortedSet.Builder<V> foosBuilder(Comparator<V>)}, where the {@code AutoValue}
* class has a property called {@code foos} with a type whose builder can be made with an
* argument of the given type.
* </ul>
*/
private void classifyMethodOneArg(ExecutableElement method) {
if (classifyPropertyBuilderOneArg(method)) {
return;
}
String methodName = method.getSimpleName().toString();
ImmutableMap<String, E> propertyElements = propertyElements();
String propertyName = null;
E propertyElement = propertyElements.get(methodName);
Multimap<String, PropertySetter> propertyNameToSetters = null;
if (propertyElement != null) {
propertyNameToSetters = propertyNameToUnprefixedSetters;
propertyName = methodName;
} else if (methodName.startsWith("set") && methodName.length() > 3) {
propertyNameToSetters = propertyNameToPrefixedSetters;
propertyName = PropertyNames.decapitalizeLikeJavaBeans(methodName.substring(3));
propertyElement = propertyElements.get(propertyName);
if (propertyElement == null) {
// If our property is defined by a getter called getOAuth() then it is called "OAuth"
// because of JavaBeans rules. Therefore we want JavaBeans rules to be used for the setter
// too, so that you can write setOAuth(x). Meanwhile if the property is defined by a getter
// called oAuth() then it is called "oAuth", but you would still expect to be able to set it
// using setOAuth(x). Hence the second try using a decapitalize method without the quirky
// two-leading-capitals rule.
propertyName = PropertyNames.decapitalizeNormally(methodName.substring(3));
propertyElement = propertyElements.get(propertyName);
}
} else {
// We might also have an unprefixed setter, so the getter is called OAuth() or getOAuth() and
// the setter is called oAuth(x), where again JavaBeans rules imply that it should be called
// OAuth(x). Iterating over the properties here is a bit clunky but this case should be
// unusual.
propertyNameToSetters = propertyNameToUnprefixedSetters;
for (Map.Entry<String, E> entry : propertyElements.entrySet()) {
if (methodName.equals(PropertyNames.decapitalizeNormally(entry.getKey()))) {
propertyName = entry.getKey();
propertyElement = entry.getValue();
break;
}
}
}
if (propertyElement == null || propertyNameToSetters == null) {
// The second disjunct isn't needed but convinces control-flow checkers that
// propertyNameToSetters can't be null when we call put on it below.
errorReporter.reportError(method, "[%sBuilderWhatProp] Method %s does not correspond to %s", autoWhat(), methodName, getterMustMatch());
checkForFailedJavaBean(method);
return;
}
Optional<Copier> function = getSetterFunction(propertyElement, method);
if (function.isPresent()) {
DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType());
ExecutableType methodMirror = MoreTypes.asExecutable(typeUtils.asMemberOf(builderTypeMirror, method));
TypeMirror returnType = methodMirror.getReturnType();
if (typeUtils.isSubtype(builderType.asType(), returnType) && !MoreTypes.isTypeOf(Object.class, returnType)) {
// We allow the return type to be a supertype (other than Object), to support step builders.
TypeMirror parameterType = Iterables.getOnlyElement(methodMirror.getParameterTypes());
propertyNameToSetters.put(propertyName, new PropertySetter(method, parameterType, function.get()));
} else {
errorReporter.reportError(method, "[%sBuilderRet] Setter methods must return %s or a supertype", autoWhat(), builderType.asType());
}
}
}
use of com.google.auto.value.processor.BuilderSpec.Copier in project auto by google.
the class BuilderMethodClassifier method getConvertingSetterFunction.
/**
* Returns an {@code Optional} containing a function to use {@code copyOfMethod} to copy the
* {@code parameterType} to the {@code targetType}, or {@code Optional.empty()} if the method
* can't be used. For example, we might have a property of type {@code ImmutableSet<T>} and our
* setter has a parameter of type {@code Set<? extends T>}. Can we use {@code ImmutableSet<E>
* ImmutableSet.copyOf(Collection<? extends E>)} to set the property? What about {@code
* ImmutableSet<E> ImmutableSet.copyOf(E[])}?
*
* <p>The example here is deliberately complicated, in that it has a type parameter of its own,
* presumably because the {@code @AutoValue} class is {@code Foo<T>}. One subtle point is that the
* builder will then be {@code Builder<T>} where this {@code T} is a <i>different</i> type
* variable. However, we've used {@link TypeVariables} to ensure that the {@code T} in {@code
* ImmutableSet<T>} is actually the one from {@code Builder<T>} instead of the original one from
* {@code Foo<T>}.}
*
* @param copyOfMethod the candidate method to do the copy, {@code
* ImmutableSet.copyOf(Collection<? extends E>)} or {@code ImmutableSet.copyOf(E[])} in the
* examples.
* @param targetType the type of the property to be set, {@code ImmutableSet<T>} in the example.
* @param parameterType the type of the setter parameter, {@code Set<? extends T>} in the example.
* @return a function that maps a string parameter to a method call using that parameter. For
* example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}.
*/
private Optional<Copier> getConvertingSetterFunction(ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) {
// Instead, we do the variable substitutions ourselves.
if (TypeVariables.canAssignStaticMethodResult(copyOfMethod, parameterType, targetType, typeUtils)) {
String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName();
Function<String, String> callMethod = s -> method + "(" + s + ")";
// This is a big old hack. We guess that the method can accept a null parameter if it has
// "Nullable" in the name, which java.util.Optional.ofNullable and
// com.google.common.base.Optional.fromNullable do.
Copier copier = method.contains("Nullable") ? Copier.acceptingNull(callMethod) : Copier.notAcceptingNull(callMethod);
return Optional.of(copier);
}
return Optional.empty();
}
Aggregations