private boolean callsWritingConstructor(
        MethodDeclaration methodDeclaration,
        HashSet writingConstructorBindings,
        Set visitedMethodDeclarations) {
      Block body = methodDeclaration.getBody();
      if (body == null) return false;

      List statements = body.statements();
      if (statements.size() == 0) return false;

      Statement statement = (Statement) statements.get(0);
      if (!(statement instanceof ConstructorInvocation)) return false;

      ConstructorInvocation invocation = (ConstructorInvocation) statement;
      IMethodBinding constructorBinding = invocation.resolveConstructorBinding();
      if (constructorBinding == null) return false;

      if (writingConstructorBindings.contains(constructorBinding)) {
        return true;
      } else {
        ASTNode declaration =
            ASTNodes.findDeclaration(constructorBinding, methodDeclaration.getParent());
        if (!(declaration instanceof MethodDeclaration)) return false;

        if (visitedMethodDeclarations.contains(declaration)) {
          return false;
        }
        visitedMethodDeclarations.add(methodDeclaration);
        return callsWritingConstructor(
            (MethodDeclaration) declaration, writingConstructorBindings, visitedMethodDeclarations);
      }
    }
  @Override
  public void run(IProgressMonitor monitor) throws CoreException {
    if (monitor == null) monitor = new NullProgressMonitor();
    try {
      monitor.beginTask("", 1); // $NON-NLS-1$
      monitor.setTaskName(CodeGenerationMessages.GenerateToStringOperation_description);

      AbstractTypeDeclaration declaration =
          (AbstractTypeDeclaration)
              ASTNodes.findDeclaration(fContext.getTypeBinding(), fRewrite.getRoot());
      ListRewrite rewriter =
          fRewrite
              .getASTRewrite()
              .getListRewrite(declaration, declaration.getBodyDeclarationsProperty());
      if (fContext.getTypeBinding() != null && rewriter != null) {

        MethodDeclaration toStringMethod = fGenerator.generateToStringMethod();

        List<BodyDeclaration> list = declaration.bodyDeclarations();
        BodyDeclaration replace = findMethodToReplace(list, toStringMethod);
        if (replace == null
            || ((Boolean)
                    toStringMethod.getProperty(AbstractToStringGenerator.OVERWRITE_METHOD_PROPERTY))
                .booleanValue()) insertMethod(toStringMethod, rewriter, replace);

        List<MethodDeclaration> helperMethods = fGenerator.generateHelperMethods();
        for (Iterator<MethodDeclaration> iterator = helperMethods.iterator();
            iterator.hasNext(); ) {
          MethodDeclaration method = iterator.next();
          replace = findMethodToReplace(list, method);
          if (replace == null
              || ((Boolean) method.getProperty(AbstractToStringGenerator.OVERWRITE_METHOD_PROPERTY))
                  .booleanValue()) {
            insertMethod(method, rewriter, replace);
          }
        }

        JavaModelUtil.applyEdit(
            (ICompilationUnit) fUnit.getJavaElement(),
            fRewrite.createChange(true).getEdit(),
            false,
            monitor);
      }

    } finally {
      monitor.done();
    }
  }
  private void computeOutput(RefactoringStatus status) {
    // First find all writes inside the selection.
    FlowContext flowContext = new FlowContext(0, fMaxVariableId + 1);
    flowContext.setConsiderAccessMode(true);
    flowContext.setComputeMode(FlowContext.RETURN_VALUES);
    FlowInfo returnInfo = new InOutFlowAnalyzer(flowContext).perform(getSelectedNodes());
    IVariableBinding[] returnValues =
        returnInfo.get(flowContext, FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN);

    // Compute a selection that exactly covers the selected nodes
    IRegion region = getSelectedNodeRange();
    Selection selection = Selection.createFromStartLength(region.getOffset(), region.getLength());

    List<IVariableBinding> localReads = new ArrayList<>();
    flowContext.setComputeMode(FlowContext.ARGUMENTS);
    FlowInfo argInfo =
        new InputFlowAnalyzer(flowContext, selection, true).perform(fEnclosingBodyDeclaration);
    IVariableBinding[] reads =
        argInfo.get(flowContext, FlowInfo.READ | FlowInfo.READ_POTENTIAL | FlowInfo.UNKNOWN);
    outer:
    for (int i = 0; i < returnValues.length && localReads.size() < returnValues.length; i++) {
      IVariableBinding binding = returnValues[i];
      for (int x = 0; x < reads.length; x++) {
        if (reads[x] == binding) {
          localReads.add(binding);
          fReturnValue = binding;
          continue outer;
        }
      }
    }
    switch (localReads.size()) {
      case 0:
        fReturnValue = null;
        break;
      case 1:
        break;
      default:
        fReturnValue = null;
        StringBuffer affectedLocals = new StringBuffer();
        for (int i = 0; i < localReads.size(); i++) {
          IVariableBinding binding = localReads.get(i);
          String bindingName =
              BindingLabelProvider.getBindingLabel(
                  binding,
                  BindingLabelProvider.DEFAULT_TEXTFLAGS | JavaElementLabels.F_PRE_TYPE_SIGNATURE);
          affectedLocals.append(bindingName);
          if (i != localReads.size() - 1) {
            affectedLocals.append('\n');
          }
        }
        String message =
            MessageFormat.format(
                RefactoringCoreMessages.ExtractMethodAnalyzer_assignments_to_local,
                new Object[] {affectedLocals.toString()});
        status.addFatalError(message, JavaStatusContext.create(fCUnit, getSelection()));
        return;
    }
    List<IVariableBinding> callerLocals = new ArrayList<>(5);
    FlowInfo localInfo =
        new InputFlowAnalyzer(flowContext, selection, false).perform(fEnclosingBodyDeclaration);
    IVariableBinding[] writes =
        localInfo.get(flowContext, FlowInfo.WRITE | FlowInfo.WRITE_POTENTIAL | FlowInfo.UNKNOWN);
    for (int i = 0; i < writes.length; i++) {
      IVariableBinding write = writes[i];
      if (getSelection().covers(ASTNodes.findDeclaration(write, fEnclosingBodyDeclaration)))
        callerLocals.add(write);
    }
    fCallerLocals = callerLocals.toArray(new IVariableBinding[callerLocals.size()]);
    if (fReturnValue != null
        && getSelection().covers(ASTNodes.findDeclaration(fReturnValue, fEnclosingBodyDeclaration)))
      fReturnLocal = fReturnValue;
  }