docs: private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List<BuilderFieldData> builderFields,
  			EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) {
  		
  		ASTNode source = sourceNode.get();
  		
  		TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get());
  		long p = (long) source.sourceStart << 32 | source.sourceEnd;
  		
  		ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult);
  		
  		constructor.modifiers = toEclipseModifier(AccessLevel.PROTECTED);
  		constructor.selector = typeDeclaration.name;
  		if (callBuilderBasedSuperConstructor) {
  			constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
  			constructor.constructorCall.arguments = new Expression[] {new SingleNameReference(BUILDER_VARIABLE_NAME, p)};
  		} else {
  			constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
  		}
  		constructor.constructorCall.sourceStart = source.sourceStart;
  		constructor.constructorCall.sourceEnd = source.sourceEnd;
  		constructor.thrownExceptions = null;
  		constructor.typeParameters = null;
  		constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
  		constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
  		constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
  		
  		TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
  		TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
  		constructor.arguments = new Argument[] {new Argument(BUILDER_VARIABLE_NAME, p, builderType, Modifier.FINAL)};
  		
  		List<Statement> statements = new ArrayList<Statement>();
  		
  		for (BuilderFieldData fieldNode : builderFields) {
  			char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode);
  			FieldReference fieldInThis = new FieldReference(fieldNode.rawName, p);
  			int s = (int) (p >> 32);
  			int e = (int) p;
  			fieldInThis.receiver = new ThisReference(s, e);
  			
  			Expression assignmentExpr;
  			if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
  				fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, BUILDER_VARIABLE_NAME_STRING);
  				assignmentExpr = new SingleNameReference(fieldNode.name, p);
  			} else {
  				char[][] variableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldName};
  				long[] positions = new long[] {p, p};
  				assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
  			}
  			Statement assignment = new Assignment(fieldInThis, assignmentExpr, (int) p);
  			
  			// In case of @Builder.Default, set the value to the default if it was NOT set in the builder.
  			if (fieldNode.nameOfSetFlag != null) {
  				char[][] setVariableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.nameOfSetFlag};
  				long[] positions = new long[] {p, p};
  				QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e);
  
  				MessageSend defaultMethodCall = new MessageSend();
  				defaultMethodCall.sourceStart = source.sourceStart;
  				defaultMethodCall.sourceEnd = source.sourceEnd;
  				defaultMethodCall.receiver = new SingleNameReference(((TypeDeclaration) typeNode.get()).name, 0L);
  				defaultMethodCall.selector = fieldNode.nameOfDefaultProvider;
  				defaultMethodCall.typeArguments = typeParameterNames(((TypeDeclaration) typeNode.get()).typeParameters);
  				
  				Statement defaultAssignment = new Assignment(fieldInThis, defaultMethodCall, (int) p);
  				IfStatement ifBlockForDefault = new IfStatement(setVariableInBuilderRef, assignment, defaultAssignment, s, e);
  				statements.add(ifBlockForDefault);
  			} else {
  				statements.add(assignment);
  			}
  			
  			if (hasNonNullAnnotations(fieldNode.originalFieldNode)) {
  				Statement nullCheck = generateNullCheck((FieldDeclaration) fieldNode.originalFieldNode.get(), sourceNode);
  				if (nullCheck != null) statements.add(nullCheck);
  			}
  		}
  		
  		constructor.statements = statements.isEmpty() ? null : statements.toArray(new Statement[0]);
  		
  		constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
  		
  		injectMethod(typeNode, constructor);
  	}