/**
  * Updates a javadoc tag, by either adding a new one or removing an existing one
  *
  * @param body
  */
 private void updateTag(BodyDeclaration body) {
   Javadoc docnode = body.getJavadoc();
   AST ast = body.getAST();
   if (docnode == null) {
     docnode = ast.newJavadoc();
     rewrite.set(body, body.getJavadocProperty(), docnode, null);
   }
   ListRewrite lrewrite = rewrite.getListRewrite(docnode, Javadoc.TAGS_PROPERTY);
   if (remove) {
     List<TagElement> tags =
         (List<TagElement>) docnode.getStructuralProperty(Javadoc.TAGS_PROPERTY);
     if (tags != null) {
       TagElement tag = null;
       for (int i = 0; i < tags.size(); i++) {
         tag = tags.get(i);
         if (tagname.equals(tag.getTagName())) {
           lrewrite.remove(tag, null);
         }
       }
     }
   } else {
     TagElement newtag = ast.newTagElement();
     newtag.setTagName(tagname);
     lrewrite.insertLast(newtag, null);
   }
 }
 private void updatePackageStatement(
     CompilationUnit astCU, String[] pkgName, ASTRewrite rewriter, ICompilationUnit cu)
     throws JavaModelException {
   boolean defaultPackage = pkgName.length == 0;
   AST ast = astCU.getAST();
   if (defaultPackage) {
     // remove existing package statement
     PackageDeclaration pkg = astCU.getPackage();
     if (pkg != null) {
       int pkgStart;
       Javadoc javadoc = pkg.getJavadoc();
       if (javadoc != null) {
         pkgStart = javadoc.getStartPosition() + javadoc.getLength() + 1;
       } else {
         pkgStart = pkg.getStartPosition();
       }
       int extendedStart = astCU.getExtendedStartPosition(pkg);
       if (pkgStart != extendedStart) {
         // keep the comments associated with package declaration
         // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=247757
         String commentSource = cu.getSource().substring(extendedStart, pkgStart);
         ASTNode comment =
             rewriter.createStringPlaceholder(commentSource, ASTNode.PACKAGE_DECLARATION);
         rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, comment, null);
       } else {
         rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, null, null);
       }
     }
   } else {
     org.eclipse.jdt.core.dom.PackageDeclaration pkg = astCU.getPackage();
     if (pkg != null) {
       // rename package statement
       Name name = ast.newName(pkgName);
       rewriter.set(pkg, PackageDeclaration.NAME_PROPERTY, name, null);
     } else {
       // create new package statement
       pkg = ast.newPackageDeclaration();
       pkg.setName(ast.newName(pkgName));
       rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, pkg, null);
     }
   }
 }
  public void run(ISelection selection) {

    String errorTitle = CompareMessages.AddFromHistory_title;
    String errorMessage = CompareMessages.AddFromHistory_internalErrorMessage;
    Shell shell = getShell();

    ICompilationUnit cu = null;
    IParent parent = null;
    IMember input = null;

    // analyze selection
    if (selection.isEmpty()) {
      // no selection: we try to use the editor's input
      JavaEditor editor = getEditor();
      if (editor != null) {
        IEditorInput editorInput = editor.getEditorInput();
        IWorkingCopyManager manager = JavaPlugin.getDefault().getWorkingCopyManager();
        if (manager != null) {
          cu = manager.getWorkingCopy(editorInput);
          parent = cu;
        }
      }
    } else {
      input = getEditionElement(selection);
      if (input != null) {
        cu = input.getCompilationUnit();
        parent = input;
        input = null;

      } else {
        if (selection instanceof IStructuredSelection) {
          Object o = ((IStructuredSelection) selection).getFirstElement();
          if (o instanceof ICompilationUnit) {
            cu = (ICompilationUnit) o;
            parent = cu;
          }
        }
      }
    }

    if (parent == null || cu == null) {
      String invalidSelectionMessage = CompareMessages.AddFromHistory_invalidSelectionMessage;
      MessageDialog.openInformation(shell, errorTitle, invalidSelectionMessage);
      return;
    }

    IFile file = getFile(parent);
    if (file == null) {
      MessageDialog.openError(shell, errorTitle, errorMessage);
      return;
    }

    boolean inEditor = beingEdited(file);

    IStatus status = Resources.makeCommittable(file, shell);
    if (!status.isOK()) {
      return;
    }

    // get the document where to insert the text
    IPath path = file.getFullPath();
    ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
    ITextFileBuffer textFileBuffer = null;
    try {
      bufferManager.connect(path, LocationKind.IFILE, null);
      textFileBuffer = bufferManager.getTextFileBuffer(path, LocationKind.IFILE);
      IDocument document = textFileBuffer.getDocument();

      // configure EditionSelectionDialog and let user select an edition
      ITypedElement target = new JavaTextBufferNode(file, document, inEditor);
      ITypedElement[] editions = buildEditions(target, file);

      ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME);
      EditionSelectionDialog d = new EditionSelectionDialog(shell, bundle);
      d.setAddMode(true);
      d.setHelpContextId(IJavaHelpContextIds.ADD_ELEMENT_FROM_HISTORY_DIALOG);
      ITypedElement selected = d.selectEdition(target, editions, parent);
      if (selected == null) return; // user cancel

      ICompilationUnit cu2 = cu;
      if (parent instanceof IMember) cu2 = ((IMember) parent).getCompilationUnit();

      CompilationUnit root = parsePartialCompilationUnit(cu2);
      ASTRewrite rewriter = ASTRewrite.create(root.getAST());

      ITypedElement[] results = d.getSelection();
      for (int i = 0; i < results.length; i++) {

        // create an AST node
        ASTNode newNode =
            createASTNode(
                rewriter,
                results[i],
                TextUtilities.getDefaultLineDelimiter(document),
                cu.getJavaProject());
        if (newNode == null) {
          MessageDialog.openError(shell, errorTitle, errorMessage);
          return;
        }

        // now determine where to put the new node
        if (newNode instanceof PackageDeclaration) {
          rewriter.set(root, CompilationUnit.PACKAGE_PROPERTY, newNode, null);

        } else if (newNode instanceof ImportDeclaration) {
          ListRewrite lw = rewriter.getListRewrite(root, CompilationUnit.IMPORTS_PROPERTY);
          lw.insertFirst(newNode, null);

        } else { // class, interface, enum, annotation, method, field

          if (parent instanceof ICompilationUnit) { // top level
            ListRewrite lw = rewriter.getListRewrite(root, CompilationUnit.TYPES_PROPERTY);
            int index = ASTNodes.getInsertionIndex((BodyDeclaration) newNode, root.types());
            lw.insertAt(newNode, index, null);

          } else if (parent instanceof IType) {
            ASTNode declaration = getBodyContainer(root, (IType) parent);
            if (declaration instanceof TypeDeclaration
                || declaration instanceof AnnotationTypeDeclaration) {
              List container = ASTNodes.getBodyDeclarations(declaration);
              int index = ASTNodes.getInsertionIndex((BodyDeclaration) newNode, container);
              ListRewrite lw =
                  rewriter.getListRewrite(
                      declaration, ASTNodes.getBodyDeclarationsProperty(declaration));
              lw.insertAt(newNode, index, null);
            } else if (declaration instanceof EnumDeclaration) {
              List container = ((EnumDeclaration) declaration).enumConstants();
              int index = ASTNodes.getInsertionIndex((FieldDeclaration) newNode, container);
              ListRewrite lw =
                  rewriter.getListRewrite(declaration, EnumDeclaration.ENUM_CONSTANTS_PROPERTY);
              lw.insertAt(newNode, index, null);
            }
          } else {
            JavaPlugin.logErrorMessage(
                "JavaAddElementFromHistoryImpl: unknown container " + parent); // $NON-NLS-1$
          }
        }
      }

      Map options = null;
      IJavaProject javaProject = cu2.getJavaProject();
      if (javaProject != null) options = javaProject.getOptions(true);
      applyChanges(rewriter, document, textFileBuffer, shell, inEditor, options);

    } catch (InvocationTargetException ex) {
      ExceptionHandler.handle(ex, shell, errorTitle, errorMessage);

    } catch (InterruptedException ex) {
      // shouldn't be called because is not cancelable
      Assert.isTrue(false);

    } catch (CoreException ex) {
      ExceptionHandler.handle(ex, shell, errorTitle, errorMessage);

    } finally {
      try {
        if (textFileBuffer != null) bufferManager.disconnect(path, LocationKind.IFILE, null);
      } catch (CoreException e) {
        JavaPlugin.log(e);
      }
    }
  }
  private ASTRewrite doAddLocal(CompilationUnit cu) {
    AST ast = cu.getAST();

    Block body;
    BodyDeclaration decl = ASTResolving.findParentBodyDeclaration(fOriginalNode);
    IBinding targetContext = null;
    if (decl instanceof MethodDeclaration) {
      body = (((MethodDeclaration) decl).getBody());
      targetContext = ((MethodDeclaration) decl).resolveBinding();
    } else if (decl instanceof Initializer) {
      body = (((Initializer) decl).getBody());
      targetContext = Bindings.getBindingOfParentType(decl);
    } else {
      return null;
    }
    ASTRewrite rewrite = ASTRewrite.create(ast);

    ImportRewrite imports = createImportRewrite((CompilationUnit) decl.getRoot());
    ImportRewriteContext importRewriteContext =
        new ContextSensitiveImportRewriteContext(decl, imports);

    SimpleName[] names = getAllReferences(body);
    ASTNode dominant = getDominantNode(names);

    Statement dominantStatement = ASTResolving.findParentStatement(dominant);
    if (ASTNodes.isControlStatementBody(dominantStatement.getLocationInParent())) {
      dominantStatement = (Statement) dominantStatement.getParent();
    }

    SimpleName node = names[0];

    if (isAssigned(dominantStatement, node)) {
      // x = 1; -> int x = 1;
      Assignment assignment = (Assignment) node.getParent();

      // trick to avoid comment removal around the statement: keep the expression statement
      // and replace the assignment with an VariableDeclarationExpression
      VariableDeclarationFragment newDeclFrag = ast.newVariableDeclarationFragment();
      VariableDeclarationExpression newDecl = ast.newVariableDeclarationExpression(newDeclFrag);
      newDecl.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext));

      Expression placeholder = (Expression) rewrite.createCopyTarget(assignment.getRightHandSide());
      newDeclFrag.setInitializer(placeholder);
      newDeclFrag.setName(ast.newSimpleName(node.getIdentifier()));
      rewrite.replace(assignment, newDecl, null);

      addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
      addLinkedPosition(rewrite.track(newDeclFrag.getName()), true, KEY_NAME);

      setEndPosition(rewrite.track(assignment.getParent()));

      return rewrite;
    } else if ((dominant != dominantStatement) && isForStatementInit(dominantStatement, node)) {
      //	for (x = 1;;) ->for (int x = 1;;)

      Assignment assignment = (Assignment) node.getParent();

      VariableDeclarationFragment frag = ast.newVariableDeclarationFragment();
      VariableDeclarationExpression expression = ast.newVariableDeclarationExpression(frag);
      frag.setName(ast.newSimpleName(node.getIdentifier()));
      Expression placeholder = (Expression) rewrite.createCopyTarget(assignment.getRightHandSide());
      frag.setInitializer(placeholder);
      expression.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext));

      rewrite.replace(assignment, expression, null);

      addLinkedPosition(rewrite.track(expression.getType()), false, KEY_TYPE);
      addLinkedPosition(rewrite.track(frag.getName()), true, KEY_NAME);

      setEndPosition(rewrite.track(expression));

      return rewrite;

    } else if ((dominant != dominantStatement)
        && isEnhancedForStatementVariable(dominantStatement, node)) {
      //	for (x: collectionOfT) -> for (T x: collectionOfT)

      EnhancedForStatement enhancedForStatement = (EnhancedForStatement) dominantStatement;
      SingleVariableDeclaration parameter = enhancedForStatement.getParameter();
      Expression expression = enhancedForStatement.getExpression();

      SimpleName newName = (SimpleName) rewrite.createMoveTarget(node);
      rewrite.set(parameter, SingleVariableDeclaration.NAME_PROPERTY, newName, null);

      ITypeBinding elementBinding = null;
      ITypeBinding typeBinding = expression.resolveTypeBinding();
      if (typeBinding != null) {
        if (typeBinding.isArray()) {
          elementBinding = typeBinding.getElementType();
        } else {
          ITypeBinding iterable =
              Bindings.findTypeInHierarchy(typeBinding, "java.lang.Iterable"); // $NON-NLS-1$
          if (iterable != null) {
            ITypeBinding[] typeArguments = iterable.getTypeArguments();
            if (typeArguments.length == 1) {
              elementBinding = typeArguments[0];
              elementBinding = Bindings.normalizeForDeclarationUse(elementBinding, ast);
            }
          }
        }
      }
      Type type;
      if (elementBinding != null) {
        type = imports.addImport(elementBinding, ast, importRewriteContext);
      } else {
        type = ast.newSimpleType(ast.newSimpleName("Object")); // $NON-NLS-1$
      }

      rewrite.set(parameter, SingleVariableDeclaration.TYPE_PROPERTY, type, null);

      addLinkedPosition(rewrite.track(type), false, KEY_TYPE);
      addLinkedPosition(rewrite.track(newName), true, KEY_NAME);

      setEndPosition(rewrite.track(expression));

      return rewrite;
    }

    //	foo(x) -> int x; foo(x)

    VariableDeclarationFragment newDeclFrag = ast.newVariableDeclarationFragment();
    VariableDeclarationStatement newDecl = ast.newVariableDeclarationStatement(newDeclFrag);

    newDeclFrag.setName(ast.newSimpleName(node.getIdentifier()));
    newDecl.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext));
    //		newDeclFrag.setInitializer(ASTNodeFactory.newDefaultExpression(ast, newDecl.getType(), 0));

    addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
    addLinkedPosition(rewrite.track(node), true, KEY_NAME);
    addLinkedPosition(rewrite.track(newDeclFrag.getName()), false, KEY_NAME);

    Statement statement = dominantStatement;
    List<? extends ASTNode> list = ASTNodes.getContainingList(statement);
    while (list == null
        && statement.getParent() instanceof Statement) { // parent must be if, for or while
      statement = (Statement) statement.getParent();
      list = ASTNodes.getContainingList(statement);
    }
    if (list != null) {
      ASTNode parent = statement.getParent();
      StructuralPropertyDescriptor childProperty = statement.getLocationInParent();
      if (childProperty.isChildListProperty()) {
        rewrite
            .getListRewrite(parent, (ChildListPropertyDescriptor) childProperty)
            .insertBefore(newDecl, statement, null);
        return rewrite;
      } else {
        return null;
      }
    }
    return rewrite;
  }
  /*(non-Javadoc)
   * @see org.eclipse.jdt.internal.ui.text.correction.ASTRewriteCorrectionProposal#getRewrite()
   */
  @Override
  protected ASTRewrite getRewrite() {
    AST ast = fMethodDecl.getAST();

    ITypeBinding returnBinding = getReturnTypeBinding();

    if (fExistingReturn != null) {
      ASTRewrite rewrite = ASTRewrite.create(ast);

      Expression expression =
          evaluateReturnExpressions(ast, returnBinding, fExistingReturn.getStartPosition());
      if (expression != null) {
        rewrite.set(fExistingReturn, ReturnStatement.EXPRESSION_PROPERTY, expression, null);

        addLinkedPosition(rewrite.track(expression), true, RETURN_EXPRESSION_KEY);
      }
      return rewrite;
    } else {
      ASTRewrite rewrite = ASTRewrite.create(ast);

      Block block = fMethodDecl.getBody();

      List<Statement> statements = block.statements();
      int nStatements = statements.size();
      ASTNode lastStatement = null;
      if (nStatements > 0) {
        lastStatement = statements.get(nStatements - 1);
      }

      if (returnBinding != null
          && lastStatement instanceof ExpressionStatement
          && lastStatement.getNodeType() != ASTNode.ASSIGNMENT) {
        Expression expression = ((ExpressionStatement) lastStatement).getExpression();
        ITypeBinding binding = expression.resolveTypeBinding();
        if (binding != null && binding.isAssignmentCompatible(returnBinding)) {
          Expression placeHolder = (Expression) rewrite.createMoveTarget(expression);

          ReturnStatement returnStatement = ast.newReturnStatement();
          returnStatement.setExpression(placeHolder);

          rewrite.replace(lastStatement, returnStatement, null);
          return rewrite;
        }
      }

      int offset;
      if (lastStatement == null) {
        offset = block.getStartPosition() + 1;
      } else {
        offset = lastStatement.getStartPosition() + lastStatement.getLength();
      }
      ReturnStatement returnStatement = ast.newReturnStatement();
      Expression expression = evaluateReturnExpressions(ast, returnBinding, offset);

      returnStatement.setExpression(expression);

      rewrite.getListRewrite(block, Block.STATEMENTS_PROPERTY).insertLast(returnStatement, null);

      addLinkedPosition(
          rewrite.track(returnStatement.getExpression()), true, RETURN_EXPRESSION_KEY);
      return rewrite;
    }
  }
    private void insertMissingJavadocTag(
        ASTRewrite rewrite, ASTNode missingNode, BodyDeclaration bodyDecl) {
      AST ast = bodyDecl.getAST();
      Javadoc javadoc = bodyDecl.getJavadoc();
      if (javadoc == null) {
        javadoc = ast.newJavadoc();
        rewrite.set(bodyDecl, bodyDecl.getJavadocProperty(), javadoc, null);
      }

      ListRewrite tagsRewriter = rewrite.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY);

      StructuralPropertyDescriptor location = missingNode.getLocationInParent();
      TagElement newTag;
      if (location == SingleVariableDeclaration.NAME_PROPERTY) {
        // normal parameter
        SingleVariableDeclaration decl = (SingleVariableDeclaration) missingNode.getParent();

        String name = ((SimpleName) missingNode).getIdentifier();
        newTag = ast.newTagElement();
        newTag.setTagName(TagElement.TAG_PARAM);
        newTag.fragments().add(ast.newSimpleName(name));

        MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDecl;
        List<SingleVariableDeclaration> params = methodDeclaration.parameters();

        Set<String> sameKindLeadingNames = getPreviousParamNames(params, decl);

        List<TypeParameter> typeParams = methodDeclaration.typeParameters();
        for (int i = 0; i < typeParams.size(); i++) {
          String curr = '<' + typeParams.get(i).getName().getIdentifier() + '>';
          sameKindLeadingNames.add(curr);
        }
        insertTag(tagsRewriter, newTag, sameKindLeadingNames);
      } else if (location == TypeParameter.NAME_PROPERTY) {
        // type parameter
        TypeParameter typeParam = (TypeParameter) missingNode.getParent();

        String name = '<' + ((SimpleName) missingNode).getIdentifier() + '>';
        newTag = ast.newTagElement();
        newTag.setTagName(TagElement.TAG_PARAM);
        TextElement text = ast.newTextElement();
        text.setText(name);
        newTag.fragments().add(text);
        List<TypeParameter> params;
        if (bodyDecl instanceof TypeDeclaration) {
          params = ((TypeDeclaration) bodyDecl).typeParameters();
        } else {
          params = ((MethodDeclaration) bodyDecl).typeParameters();
        }
        insertTag(tagsRewriter, newTag, getPreviousTypeParamNames(params, typeParam));
      } else if (location == MethodDeclaration.RETURN_TYPE2_PROPERTY) {
        newTag = ast.newTagElement();
        newTag.setTagName(TagElement.TAG_RETURN);
        insertTag(tagsRewriter, newTag, null);
      } else if (location == MethodDeclaration.THROWN_EXCEPTION_TYPES_PROPERTY) {
        newTag = ast.newTagElement();
        newTag.setTagName(TagElement.TAG_THROWS);
        TextElement excNode = ast.newTextElement();
        excNode.setText(ASTNodes.getQualifiedTypeName((Type) missingNode));
        newTag.fragments().add(excNode);
        List<Type> exceptions = ((MethodDeclaration) bodyDecl).thrownExceptionTypes();
        insertTag(tagsRewriter, newTag, getPreviousExceptionNames(exceptions, missingNode));
      } else {
        Assert.isTrue(
            false, "AddMissingJavadocTagProposal: unexpected node location"); // $NON-NLS-1$
        return;
      }

      TextElement textElement = ast.newTextElement();
      textElement.setText(""); // $NON-NLS-1$
      newTag.fragments().add(textElement);

      addLinkedPosition(rewrite.track(textElement), false, "comment_start"); // $NON-NLS-1$

      if (bodyDecl.getJavadoc() == null) {
        // otherwise the linked position spans over a line delimiter
        newTag.fragments().add(ast.newTextElement());
      }
    }