List of usage examples for org.eclipse.jdt.internal.compiler.ast QualifiedTypeReference getLastToken
@Override public char[] getLastToken()
From source file:lombok.eclipse.handlers.HandleSuperBuilder.java
License:Open Source License
@Override public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder"); long p = (long) ast.sourceStart << 32 | ast.sourceEnd; SuperBuilder superbuilderAnnotation = annotation.getInstance(); String builderMethodName = superbuilderAnnotation.builderMethodName(); String buildMethodName = superbuilderAnnotation.buildMethodName(); if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; boolean generateBuilderMethod; if (builderMethodName.isEmpty()) { generateBuilderMethod = false;/* w w w.ja v a 2 s . c o m*/ } else if (!checkName("builderMethodName", builderMethodName, annotationNode)) { return; } else { generateBuilderMethod = true; } if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; boolean toBuilder = superbuilderAnnotation.toBuilder(); EclipseNode tdParent = annotationNode.up(); java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>(); TypeReference returnType; TypeParameter[] typeParams; boolean addCleaning = false; if (!(tdParent.get() instanceof TypeDeclaration)) { annotationNode.addError("@SuperBuilder is only supported on types."); return; } TypeDeclaration td = (TypeDeclaration) tdParent.get(); // Gather all fields of the class that should be set by the builder. List<EclipseNode> allFields = new ArrayList<EclipseNode>(); List<EclipseNode> nonFinalNonDefaultedFields = null; boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent)); for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); Annotation[] copyableAnnotations = findCopyableAnnotations(fieldNode); BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.annotations = copyAnnotations(fd, copyableAnnotations); bfd.type = fd.type; bfd.singularData = getSingularData(fieldNode, ast); bfd.originalFieldNode = fieldNode; if (bfd.singularData != null && isDefault != null) { isDefault.addError("@Builder.Default and @Singular cannot be mixed."); isDefault = null; } if (fd.initialization == null && isDefault != null) { isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); isDefault = null; } if (fd.initialization != null && isDefault == null) { if (isFinal) continue; if (nonFinalNonDefaultedFields == null) nonFinalNonDefaultedFields = new ArrayList<EclipseNode>(); nonFinalNonDefaultedFields.add(fieldNode); } if (isDefault != null) { bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name); bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX); MethodDeclaration md = HandleBuilder.generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); if (md != null) injectMethod(tdParent, md); } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.add(fieldNode); } // Set the names of the builder classes. String builderClassName = String.valueOf(td.name) + "Builder"; String builderImplClassName = builderClassName + "Impl"; typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0]; returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p); // <C, B> are the generics for our builder. String classGenericName = "C"; String builderGenericName = "B"; // If these generics' names collide with any generics on the annotated class, modify them. // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder. java.util.List<String> typeParamStrings = new ArrayList<String>(); for (TypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.toString()); classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings); builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings); TypeReference extendsClause = td.superclass; TypeReference superclassBuilderClass = null; TypeReference[] typeArguments = new TypeReference[] { new SingleTypeReference(classGenericName.toCharArray(), 0), new SingleTypeReference(builderGenericName.toCharArray(), 0) }; if (extendsClause instanceof QualifiedTypeReference) { QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference) extendsClause; String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken()); String superclassBuilderClassName = superclassClassName + "Builder"; char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1); tokens[tokens.length] = superclassBuilderClassName.toCharArray(); long[] poss = new long[tokens.length]; Arrays.fill(poss, p); TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause); // Every token may potentially have type args. Here, we only have // type args for the last token, the superclass' builder. TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; typeArgsForTokens[typeArgsForTokens.length - 1] = mergeTypeReferences(superclassTypeArgs, typeArguments); superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } else if (extendsClause != null) { String superClass = String.valueOf(extendsClause.getTypeName()[0]); String superclassBuilderClassName = superClass + "Builder"; char[][] tokens = new char[][] { superClass.toCharArray(), superclassBuilderClassName.toCharArray() }; long[] poss = new long[tokens.length]; Arrays.fill(poss, p); TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause); // Every token may potentially have type args. Here, we only have // type args for the last token, the superclass' builder. TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][]; typeArgsForTokens[typeArgsForTokens.length - 1] = mergeTypeReferences(superclassTypeArgs, typeArguments); superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss); } // If there is no superclass, superclassBuilderClassExpression is still == null at this point. // You can use it to check whether to inherit or not. generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName, superclassBuilderClass != null); // Create the abstract builder class, or reuse an existing one. EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass, typeParams, ast, classGenericName, builderGenericName); } else { TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); if ((builderTypeDeclaration.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract)) == 0) { annotationNode.addError("Existing Builder must be an abstract static inner class."); return; } sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); // Generate errors for @Singular BFDs that have one already defined node. for (BuilderFieldData bfd : builderFields) { SingularData sd = bfd.singularData; if (sd == null) continue; EclipseSingularizer singularizer = sd.getSingularizer(); if (singularizer == null) continue; if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) { bfd.singularData = null; } } } // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary. for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { if (bfd.singularData.getSingularizer().requiresCleaning()) { addCleaning = true; break; } } if (bfd.obtainVia != null) { if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) { bfd.obtainViaNode.addError( "The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\")."); return; } if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) { bfd.obtainViaNode .addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set."); return; } } } // Generate the fields in the abstract builder class that hold the values for the instance. generateBuilderFields(builderType, builderFields, ast); if (addCleaning) { FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1); cleanDecl.declarationSourceEnd = -1; cleanDecl.modifiers = ClassFileConstants.AccPrivate; cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); injectFieldAndMarkGenerated(builderType, cleanDecl); } if (toBuilder) { // Generate $fillValuesFrom() method in the abstract builder. injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams)); // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class. injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast)); } // Generate abstract self() and build() methods in the abstract builder. injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName)); injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast)); // Create the setter methods in the abstract builder. for (BuilderFieldData bfd : builderFields) { generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName); } // Create the toString() method for the abstract builder. if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>(); for (BuilderFieldData bfd : builderFields) { for (EclipseNode f : bfd.createdFields) { fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true)); } } // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder. MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD); if (md != null) { injectMethod(builderType, md); } } if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast)); boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0; if (isAbstract) { // Only non-abstract classes get the Builder implementation. return; } // Create the builder implementation class, or reuse an existing one. EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName); if (builderImplType == null) { builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast); } else { TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) builderImplType.get(); if ((builderImplTypeDeclaration.modifiers & ClassFileConstants.AccAbstract) != 0 || (builderImplTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class."); return; } sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode); } if (toBuilder) { // Add the toBuilder() method to the annotated class. switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) { case EXISTS_BY_USER: annotationNode.addWarning("Not generating toBuilder() as it already exists."); break; case NOT_EXISTS: injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast)); default: // Should not happen. } } // Create the self() and build() methods in the BuilderImpl. injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p)); if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) { injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast)); } // Add the builder() method to the annotated class. if (generateBuilderMethod && methodExists(builderMethodName, tdParent, -1) != MemberExistsResult.NOT_EXISTS) generateBuilderMethod = false; if (generateBuilderMethod) { MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast); if (md != null) injectMethod(tdParent, md); } if (nonFinalNonDefaultedFields != null && generateBuilderMethod) { for (EclipseNode fieldNode : nonFinalNonDefaultedFields) { fieldNode.addWarning( "@SuperBuilder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); } } }