Search in sources :

Example 1 with PropertySetter

use of com.google.auto.value.processor.BuilderSpec.PropertySetter in project auto by google.

the class BuilderMethodClassifier method classifyMethods.

/**
 * Classifies the given methods and sets the state of this object based on what is found.
 */
boolean classifyMethods(Iterable<ExecutableElement> methods, boolean autoValueHasToBuilder) {
    int startErrorCount = errorReporter.errorCount();
    for (ExecutableElement method : methods) {
        classifyMethod(method);
    }
    if (errorReporter.errorCount() > startErrorCount) {
        return false;
    }
    Multimap<String, PropertySetter> propertyNameToSetter;
    if (propertyNameToPrefixedSetters.isEmpty()) {
        propertyNameToSetter = propertyNameToUnprefixedSetters;
        this.settersPrefixed = false;
    } else if (propertyNameToUnprefixedSetters.isEmpty()) {
        propertyNameToSetter = propertyNameToPrefixedSetters;
        this.settersPrefixed = true;
    } else {
        errorReporter.reportError(propertyNameToUnprefixedSetters.values().iterator().next().getSetter(), "[%sSetNotSet] If any setter methods use the setFoo convention then all must", autoWhat());
        return false;
    }
    for (String property : rewrittenPropertyTypes.keySet()) {
        TypeMirror propertyType = rewrittenPropertyTypes.get(property);
        boolean hasSetter = propertyNameToSetter.containsKey(property);
        PropertyBuilder propertyBuilder = propertyNameToPropertyBuilder.get(property);
        boolean hasBuilder = propertyBuilder != null;
        if (hasBuilder) {
            // If property bar of type Bar has a barBuilder() that returns BarBuilder, then it must
            // be possible to make a BarBuilder from a Bar if either (1) the @AutoValue class has a
            // toBuilder() or (2) there is also a setBar(Bar). Making BarBuilder from Bar is
            // possible if Bar either has a toBuilder() method or BarBuilder has an addAll or putAll
            // method that accepts a Bar argument.
            boolean canMakeBarBuilder = (propertyBuilder.getBuiltToBuilder() != null || propertyBuilder.getCopyAll() != null);
            boolean needToMakeBarBuilder = (autoValueHasToBuilder || hasSetter);
            if (needToMakeBarBuilder && !canMakeBarBuilder) {
                errorReporter.reportError(propertyBuilder.getPropertyBuilderMethod(), "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no" + " way to make that type from %2$s: %2$s does not have a non-static" + " toBuilder() method that returns %1$s, and %1$s does not have a method" + " addAll or putAll that accepts an argument of type %2$s", propertyBuilder.getBuilderTypeMirror(), propertyType);
            }
        } else if (!hasSetter && !propertiesWithDefaults.contains(property)) {
            // We have neither barBuilder() nor setBar(Bar), so we should complain.
            String setterName = settersPrefixed ? prefixWithSet(property) : property;
            errorReporter.reportError(builderType, "[%sBuilderMissingMethod] Expected a method with this signature: %s" + " %s(%s), or a %sBuilder() method", autoWhat(), builderType.asType(), setterName, propertyType, property);
        }
    }
    return errorReporter.errorCount() == startErrorCount;
}
Also used : TypeMirror(javax.lang.model.type.TypeMirror) PropertySetter(com.google.auto.value.processor.BuilderSpec.PropertySetter) ExecutableElement(javax.lang.model.element.ExecutableElement) PropertyBuilder(com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder)

Example 2 with PropertySetter

use of com.google.auto.value.processor.BuilderSpec.PropertySetter 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)

Aggregations

PropertySetter (com.google.auto.value.processor.BuilderSpec.PropertySetter)2 TypeMirror (javax.lang.model.type.TypeMirror)2 Copier (com.google.auto.value.processor.BuilderSpec.Copier)1 PropertyBuilder (com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder)1 ExecutableElement (javax.lang.model.element.ExecutableElement)1 DeclaredType (javax.lang.model.type.DeclaredType)1 ExecutableType (javax.lang.model.type.ExecutableType)1