Search in sources :

Example 1 with Copier

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();
}
Also used : ExecutableElement(javax.lang.model.element.ExecutableElement) Copier(com.google.auto.value.processor.BuilderSpec.Copier) DeclaredType(javax.lang.model.type.DeclaredType)

Example 2 with Copier

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());
        }
    }
}
Also used : ExecutableType(javax.lang.model.type.ExecutableType) TypeMirror(javax.lang.model.type.TypeMirror) PropertySetter(com.google.auto.value.processor.BuilderSpec.PropertySetter) Copier(com.google.auto.value.processor.BuilderSpec.Copier) DeclaredType(javax.lang.model.type.DeclaredType)

Example 3 with Copier

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();
}
Also used : Iterables(com.google.common.collect.Iterables) AutoValueishProcessor.nullableAnnotationFor(com.google.auto.value.processor.AutoValueishProcessor.nullableAnnotationFor) MoreTypes(com.google.auto.common.MoreTypes) Modifier(javax.lang.model.element.Modifier) VariableElement(javax.lang.model.element.VariableElement) TypeElement(javax.lang.model.element.TypeElement) Multimap(com.google.common.collect.Multimap) Function(java.util.function.Function) Elements(javax.lang.model.util.Elements) ImmutableBiMap(com.google.common.collect.ImmutableBiMap) LinkedHashMap(java.util.LinkedHashMap) PropertySetter(com.google.auto.value.processor.BuilderSpec.PropertySetter) ImmutableList(com.google.common.collect.ImmutableList) Copier(com.google.auto.value.processor.BuilderSpec.Copier) Map(java.util.Map) DeclaredType(javax.lang.model.type.DeclaredType) ElementFilter(javax.lang.model.util.ElementFilter) ImmutableMultimap(com.google.common.collect.ImmutableMultimap) LinkedHashSet(java.util.LinkedHashSet) LinkedListMultimap(com.google.common.collect.LinkedListMultimap) MoreElements(com.google.auto.common.MoreElements) ImmutableSet(com.google.common.collect.ImmutableSet) Equivalence(com.google.common.base.Equivalence) ExecutableType(javax.lang.model.type.ExecutableType) ImmutableMap(com.google.common.collect.ImmutableMap) ExecutableElement(javax.lang.model.element.ExecutableElement) Set(java.util.Set) Element(javax.lang.model.element.Element) Types(javax.lang.model.util.Types) PropertyBuilder(com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder) TypeKind(javax.lang.model.type.TypeKind) TypeMirror(javax.lang.model.type.TypeMirror) Stream(java.util.stream.Stream) ProcessingEnvironment(javax.annotation.processing.ProcessingEnvironment) Optional(java.util.Optional) Copier(com.google.auto.value.processor.BuilderSpec.Copier)

Aggregations

Copier (com.google.auto.value.processor.BuilderSpec.Copier)3 DeclaredType (javax.lang.model.type.DeclaredType)3 PropertySetter (com.google.auto.value.processor.BuilderSpec.PropertySetter)2 ExecutableElement (javax.lang.model.element.ExecutableElement)2 ExecutableType (javax.lang.model.type.ExecutableType)2 TypeMirror (javax.lang.model.type.TypeMirror)2 MoreElements (com.google.auto.common.MoreElements)1 MoreTypes (com.google.auto.common.MoreTypes)1 AutoValueishProcessor.nullableAnnotationFor (com.google.auto.value.processor.AutoValueishProcessor.nullableAnnotationFor)1 PropertyBuilder (com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder)1 Equivalence (com.google.common.base.Equivalence)1 ImmutableBiMap (com.google.common.collect.ImmutableBiMap)1 ImmutableList (com.google.common.collect.ImmutableList)1 ImmutableMap (com.google.common.collect.ImmutableMap)1 ImmutableMultimap (com.google.common.collect.ImmutableMultimap)1 ImmutableSet (com.google.common.collect.ImmutableSet)1 Iterables (com.google.common.collect.Iterables)1 LinkedListMultimap (com.google.common.collect.LinkedListMultimap)1 Multimap (com.google.common.collect.Multimap)1 LinkedHashMap (java.util.LinkedHashMap)1