Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2014 Google, Inc.
   *
   * 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 auto.parcelgson.processor;
 
 
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
Support for AutoParcel builders.

Author(s):
√Čamonn McManus
 
 class BuilderSpec {
   private static final Equivalence<TypeMirrorTYPE_EQUIVALENCE = MoreTypes.equivalence();
 
   private final TypeElement autoValueClass;
   private final Elements elementUtils;
   private final ErrorReporter errorReporter;
 
       TypeElement autoValueClass,
       ProcessingEnvironment processingEnv,
       ErrorReporter errorReporter) {
     this. = autoValueClass;
     this. = processingEnv.getElementUtils();
     this. = errorReporter;
   }
 
   private static final Set<ElementKindCLASS_OR_INTERFACE =
Determines if the @AutoParcelGson class for this instance has a correct nested @AutoParcelGson.Builder class or interface and return a representation of it in an Optional if so.
 
     Optional<TypeElementbuilderTypeElement = Optional.absent();
     for (TypeElement containedClass : ElementFilter.typesIn(.getEnclosedElements())) {
       if (MoreElements.isAnnotationPresent(containedClassAutoParcelGson.Builder.class)) {
         if (!.contains(containedClass.getKind())) {
           .reportError(
               "@AutoParcelGson.Builder can only apply to a class or an interface"containedClass);
         } else if (builderTypeElement.isPresent()) {
           .reportError(
                + " already has a Builder: " + builderTypeElement.get(),
               containedClass);
         } else {
           builderTypeElement = Optional.of(containedClass);
        }
      }
    }
    Optional<ExecutableElementvalidateMethod = Optional.absent();
    for (ExecutableElement containedMethod :
        ElementFilter.methodsIn(.getEnclosedElements())) {
      if (MoreElements.isAnnotationPresent(containedMethodAutoParcelGson.Validate.class)) {
        if (containedMethod.getModifiers().contains(.)) {
          .reportError(
              "@AutoParcelGson.Validate cannot apply to a static method"containedMethod);
        } else if (!containedMethod.getParameters().isEmpty()) {
          .reportError(
              "@AutoParcelGson.Validate method must not have parameters"containedMethod);
        } else if (containedMethod.getReturnType().getKind() != .) {
          .reportError(
              "Return type of @AutoParcelGson.Validate method must be void"containedMethod);
        } else if (validateMethod.isPresent()) {
          .reportError(
              "There can only be one @AutoParcelGson.Validate method"containedMethod);
        } else {
          validateMethod = Optional.of(containedMethod);
        }
      }
    }
    if (builderTypeElement.isPresent()) {
      return builderFrom(builderTypeElement.get(), validateMethod);
    } else {
      if (validateMethod.isPresent()) {
        .reportError(
            "@AutoParcelGson.Validate is only meaningful if there is an @AutoParcelGson.Builder",
            validateMethod.get());
      }
      return Optional.absent();
    }
  }

  
Representation of an AutoParcel.Builder class or interface.
  class Builder {
    private final TypeElement builderTypeElement;
    private final ExecutableElement buildMethod;
    private final ImmutableList<ExecutableElementsetters;
    private final Optional<ExecutableElementvalidateMethod;
    Builder(
        TypeElement builderTypeElement,
        ExecutableElement build,
        List<ExecutableElementsetters,
        Optional<ExecutableElementvalidateMethod) {
      this. = builderTypeElement;
      this. = build;
      this. = ImmutableList.copyOf(setters);
      this. = validateMethod;
    }
      return ;
    }

    
Returns a map from property name to setter method. If the setter methods are invalid (for example not every getter has a setter, or some setters don't correspond to getters) then emits an error message and returns null.

Parameters:
getterToPropertyName a list of getter methods, such as abstract String foo(); or abstract String getFoo();.
        Map<ExecutableElementStringgetterToPropertyName) {
      // Map property names to types based on the getters.
      Map<StringTypeMirrorgetterMap = new TreeMap<StringTypeMirror>();
      for (Map.Entry<ExecutableElementStringentry : getterToPropertyName.entrySet()) {
        getterMap.put(entry.getValue(), entry.getKey().getReturnType());
      }
      Map<StringExecutableElementnoPrefixMap = Maps.newLinkedHashMap();
      Map<StringExecutableElementprefixMap = Maps.newLinkedHashMap();
      boolean ok = true;
      // For each setter, check that its name and type correspond to a getter, and remove it from
      // the map if so.
      for (ExecutableElement setter : ) {
        Map<StringExecutableElementmap = noPrefixMap;
        String name = setter.getSimpleName().toString();
        TypeMirror type = getterMap.get(name);
        if (type == null && name.startsWith("set")) {
          name = Introspector.decapitalize(name.substring(3));
          type = getterMap.get(name);
          map = prefixMap;
        }
        if (type == null) {
          .reportError(
              "Method does not correspond to a property of " + setter);
          ok = false;
        } else {
          VariableElement parameter = Iterables.getOnlyElement(setter.getParameters());
          if (.equivalent(typeparameter.asType())) {
            getterMap.remove(name);
            map.put(namesetter);
          } else {
            .reportError("Parameter type should be " + typeparameter);
            ok = false;
          }
        }
      }
      if (!ok) {
        return null;
      }
      boolean prefixing = !prefixMap.isEmpty();
      if (prefixing && !noPrefixMap.isEmpty()) {
        .reportError(
            "If any setter methods use the setFoo convention then all must",
            noPrefixMap.values().iterator().next());
        return null;
      }
      // If there are any properties left in the map then we didn't see setters for them. Report
      // an error for each one separately.
      if (!getterMap.isEmpty()) {
        for (Map.Entry<StringTypeMirrorentry : getterMap.entrySet()) {
          String setterName = prefixing ? prefixWithSet(entry.getKey()) : entry.getKey();
          String error = String.format(
              "Expected a method with this signature: %s%s %s(%s)",
              ,
              TypeSimplifier.actualTypeParametersString(),
              setterName,
              entry.getValue());
          .reportError(error);
        }
        return null;
      }
      return noPrefixMap.isEmpty() ? prefixMap : noPrefixMap;
    }
    private String prefixWithSet(String propertyName) {
      // This is not internationalizationally correct, but it corresponds to what
      // Introspector.decapitalize does.
      return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
    }

    
Finds any methods in the set that return the builder type. If the builder has type parameters <A, B>, then the return type of the method must be Builder<A, B> with the same parameter names. We enforce elsewhere that the names and bounds of the builder parameters must be the same as those of the

AutoParcelGson:
class. Here's a correct example:
 @AutoParcelGson abstract class Foo<A extends Number, B> {
   abstract int someProperty();

   abstract Builder<A, B> toBuilder();

   interface Builder<A extends Number, B> {...
 }}
 

We currently impose that there cannot be more than one such method.

        Types typeUtilsSet<ExecutableElementabstractMethods) {
      ImmutableList<StringbuilderTypeParamNames =
          FluentIterable.from(.getTypeParameters())
              .transform(.)
              .toList();
      ImmutableSet.Builder<ExecutableElementmethods = ImmutableSet.builder();
      for (ExecutableElement method : abstractMethods) {
        if (.equals(typeUtils.asElement(method.getReturnType()))) {
          methods.add(method);
          DeclaredType returnType = MoreTypes.asDeclared(method.getReturnType());
          ImmutableList.Builder<StringtypeArguments = ImmutableList.builder();
          for (TypeMirror typeArgument : returnType.getTypeArguments()) {
            if (typeArgument.getKind().equals(.)) {
              typeArguments.add(typeUtils.asElement(typeArgument).getSimpleName().toString());
            }
          }
          if (!builderTypeParamNames.equals(typeArguments.build())) {
            .reportError(
                "Builder converter method should return "
                    + 
                    + TypeSimplifier.actualTypeParametersString(),
                method);
          }
        }
      }
      ImmutableSet<ExecutableElementbuilderMethods = methods.build();
      if (builderMethods.size() > 1) {
        .reportError(
            "There can be at most one builder converter method"builderMethods.iterator().next());
      }
      return builderMethods;
    }
    void defineVars(
        AutoParcelTemplateVars vars,
        TypeSimplifier typeSimplifier,
        Map<ExecutableElementStringgetterToPropertyName) {
      Map<StringExecutableElementpropertyNameToSetter = makeSetterMap(getterToPropertyName);
      if (propertyNameToSetter == null) {
        return;
      }
      vars.builderIsInterface = .getKind() == .;
      vars.builderTypeName = TypeSimplifier.classNameOf();
      vars.builderFormalTypes = typeSimplifier.formalTypeParametersString();
      vars.builderActualTypes = TypeSimplifier.actualTypeParametersString();
      vars.buildMethodName = .getSimpleName().toString();
      if (.isPresent()) {
        vars.validators = ImmutableSet.of(.get().getSimpleName().toString());
      } else {
        vars.validators = ImmutableSet.of();
      }
      ImmutableMap.Builder<StringStringsetterNameBuilder = ImmutableMap.builder();
      for (Map.Entry<StringExecutableElemententry : propertyNameToSetter.entrySet()) {
        setterNameBuilder.put(entry.getKey(), entry.getValue().getSimpleName().toString());
      }
      vars.builderSetterNames = setterNameBuilder.build();
    }
  }

  
Returns a representation of the given @AutoParcelGson.Builder class or interface. If the class or interface has abstract methods that could not be part of any builder, emits error messages and returns null.
      TypeElement builderTypeElementOptional<ExecutableElementvalidateMethod) {
    // We require the builder to have the same type parameters as the @AutoParcelGson class, meaning the
    // same names and bounds. In principle the type parameters could have different names, but that
    // would be confusing, and our code would reject it anyway because it wouldn't consider that
    // the return type of Foo<U> build() was really the same as the declaration of Foo<T>. This
    // check produces a better error message in that case and similar ones.
    boolean ok = true;
    int nTypeParameters = .getTypeParameters().size();
    if (nTypeParameters != builderTypeElement.getTypeParameters().size()) {
      ok = false;
    } else {
      for (int i = 0; i < nTypeParametersi++) {
        TypeParameterElement autoValueParam = .getTypeParameters().get(i);
        TypeParameterElement builderParam = builderTypeElement.getTypeParameters().get(i);
        if (!autoValueParam.getSimpleName().equals(builderParam.getSimpleName())) {
          ok = false;
          break;
        }
        Set<TypeMirrorautoValueBounds = new TypeMirrorSet(autoValueParam.getBounds());
        Set<TypeMirrorbuilderBounds = new TypeMirrorSet(builderParam.getBounds());
        if (!autoValueBounds.equals(builderBounds)) {
          ok = false;
          break;
        }
      }
    }
    if (!ok) {
          "Type parameters of " + builderTypeElement + " must have same names and bounds as "
              + "type parameters of " + builderTypeElement);
      return Optional.absent();
    }
    String typeParams = TypeSimplifier.actualTypeParametersString();
    List<ExecutableElementbuildMethods = new ArrayList<ExecutableElement>();
    List<ExecutableElementsetterMethods = new ArrayList<ExecutableElement>();
    // For each abstract method (in builderTypeElement or inherited), check that it is either
    // a setter method or a build method. A setter method has one argument and returns
    // builderTypeElement. A build method has no arguments and returns the @AutoParcelGson class.
    // Record each method in one of the two lists.
    for (ExecutableElement method : abstractMethods(builderTypeElement)) {
      boolean thisOk = false;
      int nParameters = method.getParameters().size();
      if (nParameters == 0
          && .equivalent(method.getReturnType(), .asType())) {
        buildMethods.add(method);
        thisOk = true;
      } else if (nParameters == 1
          && .equivalent(method.getReturnType(), builderTypeElement.asType())) {
        setterMethods.add(method);
        thisOk = true;
      }
      if (!thisOk) {
        .reportError(
            "Builder methods must either have no arguments and return "
                +  + typeParams + " or have one argument and return "
                + builderTypeElement + typeParams,
            method);
        ok = false;
      }
    }
    if (buildMethods.isEmpty()) {
          "Builder must have a single no-argument method returning "
              +  + typeParams,
          builderTypeElement);
      ok = false;
    } else if (buildMethods.size() > 1) {
      // More than one eligible build method. Emit an error for each one, that is attached to
      // that build method.
      for (ExecutableElement buildMethod : buildMethods) {
        .reportError(
            "Builder must have a single no-argument method returning "
                +  + typeParams,
            buildMethod);
      }
      ok = false;
    }
    if (ok) {
      return Optional.of(new Builder(
          builderTypeElement,
          Iterables.getOnlyElement(buildMethods),
          setterMethods,
          validateMethod));
    } else {
      return Optional.absent();
    }
  }
  // Return a list of all abstract methods in the given TypeElement or inherited from ancestors.
  private List<ExecutableElementabstractMethods(TypeElement typeElement) {
    List<ExecutableElementmethods = new ArrayList<ExecutableElement>();
    addAbstractMethods(typeElement.asType(), methods);
    return methods;
  }
  private static final TypeVisitor<ElementVoidAS_ELEMENT_VISITOR =
      new SimpleTypeVisitor6<ElementVoid>() {
        @Override protected Element defaultAction(TypeMirror eVoid p) {
          throw new IllegalArgumentException(e + "cannot be converted to an Element");
        }
        @Override public Element visitDeclared(DeclaredType tVoid p) {
          return t.asElement();
        }
        @Override public Element visitError(ErrorType tVoid p) {
          return t.asElement();
        }
        @Override public Element visitTypeVariable(TypeVariable tVoid p) {
          return t.asElement();
        }
      };
  private void addAbstractMethods(
      TypeMirror typeMirrorList<ExecutableElementabstractMethods) {
    if (typeMirror.getKind() != .) {
      return;
    }
    TypeElement typeElement = MoreElements.asType(typeMirror.accept(null));
    addAbstractMethods(typeElement.getSuperclass(), abstractMethods);
    for (TypeMirror interfaceMirror : typeElement.getInterfaces()) {
      addAbstractMethods(interfaceMirrorabstractMethods);
    }
    for (ExecutableElement method : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
      for (Iterator<ExecutableElementit = abstractMethods.iterator(); it.hasNext(); ) {
        ExecutableElement maybeOverridden = it.next();
        if (.overrides(methodmaybeOverriddentypeElement)) {
          it.remove();
        }
      }
      if (method.getModifiers().contains(.)) {
        abstractMethods.add(method);
      }
    }
  }
New to GrepCode? Check out our FAQ X