Example #1
0
  public java.util.List<JavacNode> addFieldsToBuilder(
      JavacNode builderType,
      java.util.List<Name> namesOfParameters,
      java.util.List<JCExpression> typesOfParameters,
      JCTree source) {
    int len = namesOfParameters.size();
    java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
    for (JavacNode child : builderType.down()) {
      if (child.getKind() == Kind.FIELD) existing.add(child);
    }

    java.util.List<JavacNode> out = new ArrayList<JavacNode>();

    top:
    for (int i = len - 1; i >= 0; i--) {
      Name name = namesOfParameters.get(i);
      for (JavacNode exists : existing) {
        Name n = ((JCVariableDecl) exists.get()).name;
        if (n.equals(name)) {
          out.add(exists);
          continue top;
        }
      }
      JavacTreeMaker maker = builderType.getTreeMaker();
      JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
      JCVariableDecl newField =
          maker.VarDef(
              mods,
              name,
              cloneType(maker, typesOfParameters.get(i), source, builderType.getContext()),
              null);
      out.add(injectField(builderType, newField));
    }

    Collections.reverse(out);
    return out;
  }
Example #2
0
  @Override
  public void handle(
      AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
    handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");

    Builder builderInstance = annotation.getInstance();
    String builderMethodName = builderInstance.builderMethodName();
    String buildMethodName = builderInstance.buildMethodName();
    String builderClassName = builderInstance.builderClassName();

    if (builderMethodName == null) builderMethodName = "builder";
    if (buildMethodName == null) buildMethodName = "build";
    if (builderClassName == null) builderClassName = "";

    if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
    if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
    if (!builderClassName.isEmpty()) {
      if (!checkName("builderClassName", builderClassName, annotationNode)) return;
    }

    deleteAnnotationIfNeccessary(annotationNode, Builder.class);
    deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");

    JavacNode parent = annotationNode.up();

    java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
    java.util.List<Name> namesOfParameters = new ArrayList<Name>();
    JCExpression returnType;
    List<JCTypeParameter> typeParams = List.nil();
    List<JCExpression> thrownExceptions = List.nil();
    Name nameOfStaticBuilderMethod;
    JavacNode tdParent;

    JCMethodDecl fillParametersFrom =
        parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;

    if (parent.get() instanceof JCClassDecl) {
      tdParent = parent;
      JCClassDecl td = (JCClassDecl) tdParent.get();
      ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();
      @SuppressWarnings("deprecation")
      boolean valuePresent =
          (hasAnnotation(lombok.Value.class, parent)
              || hasAnnotation(lombok.experimental.Value.class, parent));
      for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
        JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
        // final fields with an initializer cannot be written to, so they can't be 'builderized'.
        // Unfortunately presence of @Value makes
        // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this
        // math ourselves.
        // Value will only skip making a field final if it has an explicit @NonFinal annotation, so
        // we check for that.
        if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
        namesOfParameters.add(removePrefixFromField(fieldNode));
        typesOfParameters.add(fd.vartype);
        allFields.append(fieldNode);
      }

      new HandleConstructor()
          .generateConstructor(
              tdParent,
              AccessLevel.PACKAGE,
              List.<JCAnnotation>nil(),
              allFields.toList(),
              null,
              SkipIfConstructorExists.I_AM_BUILDER,
              null,
              annotationNode);

      returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
      typeParams = td.typarams;
      thrownExceptions = List.nil();
      nameOfStaticBuilderMethod = null;
      if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
    } else if (fillParametersFrom != null
        && fillParametersFrom.getName().toString().equals("<init>")) {
      if (!fillParametersFrom.typarams.isEmpty()) {
        annotationNode.addError(
            "@Builder is not supported on constructors with constructor type parameters.");
        return;
      }
      tdParent = parent.up();
      JCClassDecl td = (JCClassDecl) tdParent.get();
      returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
      typeParams = td.typarams;
      thrownExceptions = fillParametersFrom.thrown;
      nameOfStaticBuilderMethod = null;
      if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
    } else if (fillParametersFrom != null) {
      tdParent = parent.up();
      JCClassDecl td = (JCClassDecl) tdParent.get();
      if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
        annotationNode.addError(
            "@Builder is only supported on types, constructors, and static methods.");
        return;
      }
      returnType = fillParametersFrom.restype;
      typeParams = fillParametersFrom.typarams;
      thrownExceptions = fillParametersFrom.thrown;
      nameOfStaticBuilderMethod = fillParametersFrom.name;
      if (builderClassName.isEmpty()) {
        if (returnType instanceof JCTypeApply) {
          returnType = ((JCTypeApply) returnType).clazz;
        }
        if (returnType instanceof JCFieldAccess) {
          builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
        } else if (returnType instanceof JCIdent) {
          Name n = ((JCIdent) returnType).name;

          for (JCTypeParameter tp : typeParams) {
            if (tp.name.equals(n)) {
              annotationNode.addError(
                  "@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
              return;
            }
          }
          builderClassName = n.toString() + "Builder";
        } else if (returnType instanceof JCPrimitiveTypeTree) {
          builderClassName = returnType.toString() + "Builder";
          if (Character.isLowerCase(builderClassName.charAt(0))) {
            builderClassName =
                Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
          }

        } else {
          // This shouldn't happen.
          System.err.println(
              "Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: "
                  + returnType.getClass());
          builderClassName = td.name.toString() + "Builder";
        }
      }
    } else {
      annotationNode.addError(
          "@Builder is only supported on types, constructors, and static methods.");
      return;
    }

    if (fillParametersFrom != null) {
      for (JCVariableDecl param : fillParametersFrom.params) {
        namesOfParameters.add(param.name);
        typesOfParameters.add(param.vartype);
      }
    }

    JavacNode builderType = findInnerClass(tdParent, builderClassName);
    if (builderType == null) {
      builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
    } else {
      sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
    }
    java.util.List<JavacNode> fieldNodes =
        addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
    java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
    for (JavacNode fieldNode : fieldNodes) {
      JCMethodDecl newMethod =
          makeSetterMethodForBuilder(
              builderType,
              fieldNode,
              annotationNode,
              builderInstance.fluent(),
              builderInstance.chain());
      if (newMethod != null) newMethods.add(newMethod);
    }

    if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
      JCMethodDecl cd =
          HandleConstructor.createConstructor(
              AccessLevel.PACKAGE,
              List.<JCAnnotation>nil(),
              builderType,
              List.<JavacNode>nil(),
              null,
              annotationNode);
      if (cd != null) injectMethod(builderType, cd);
    }

    for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);

    if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
      JCMethodDecl md =
          generateBuildMethod(
              buildMethodName,
              nameOfStaticBuilderMethod,
              returnType,
              namesOfParameters,
              builderType,
              thrownExceptions);
      if (md != null) injectMethod(builderType, md);
    }

    if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
      JCMethodDecl md =
          HandleToString.createToString(
              builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
      if (md != null) injectMethod(builderType, md);
    }

    if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
      JCMethodDecl md =
          generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
      if (md != null) injectMethod(tdParent, md);
    }
  }