diff options
author | Sergey Prigogin | 2011-12-14 03:44:22 +0000 |
---|---|---|
committer | Sergey Prigogin | 2011-12-14 03:46:10 +0000 |
commit | c521604dd1dde08dfd55e8e08fae1de4ad6a2e21 (patch) | |
tree | c222b9837b561e15d15f9ffc8386058a2d515f31 /core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal | |
parent | 18ec28be0f857e0e6b6e7ee293b148f8dc64f21c (diff) | |
download | org.eclipse.cdt-c521604dd1dde08dfd55e8e08fae1de4ad6a2e21.tar.gz org.eclipse.cdt-c521604dd1dde08dfd55e8e08fae1de4ad6a2e21.tar.xz org.eclipse.cdt-c521604dd1dde08dfd55e8e08fae1de4ad6a2e21.zip |
Bug 363244 - Generate Getters and Setters expands macro with typedef and
breaks formatting. The bug was fixed by minimizing areas of code
affected by the refactoring changes. This minimization had a huge ripple
effect.
Diffstat (limited to 'core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal')
18 files changed, 1003 insertions, 690 deletions
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java index ea35926d435..ee28e24cea8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/ASTModificationMap.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others. + * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,6 +7,7 @@ * * Contributors: * Markus Schorn - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite; @@ -15,6 +16,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind; @@ -22,20 +24,18 @@ import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKin /** * Represents a list of modifications to an AST node. If there are nested modifications * to nodes introduced by insertions or replacements, these modifications are collected - * in separate modification maps. I.e. a modification map represents one level of - * modifications. + * in separate modification maps. I.e. a modification map represents one level of modifications. * @see ASTModificationStore * @since 5.0 */ public class ASTModificationMap { - private HashMap<IASTNode, List<ASTModification>> fModifications= new HashMap<IASTNode, List<ASTModification>>(); + private final Map<IASTNode, List<ASTModification>> fModifications= new HashMap<IASTNode, List<ASTModification>>(); /** * Adds a modification to this modification map. */ public void addModification(ASTModification mod) { - final IASTNode targetNode = mod.getKind() == ASTModification.ModificationKind.INSERT_BEFORE ? - mod.getTargetNode().getParent() : mod.getTargetNode(); + IASTNode targetNode = mod.getTargetNode(); List<ASTModification> mods= fModifications.get(targetNode); if (mods == null || mods.isEmpty()) { mods= new ArrayList<ASTModification>(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriter.java index 5b7229e5de9..dc19453bfbc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriter.java @@ -14,9 +14,13 @@ package org.eclipse.cdt.internal.core.dom.rewrite.astwriter; import org.eclipse.cdt.core.dom.ast.IASTASMDeclaration; import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIfStatement; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; @@ -34,7 +38,6 @@ import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap; */ public class ASTWriter { private ASTModificationStore modificationStore = new ASTModificationStore(); - private String givenIndentation = ""; //$NON-NLS-1$ /** * Creates a <code>ASTWriter</code>. @@ -44,16 +47,6 @@ public class ASTWriter { } /** - * Creates a <code>ASTWriter</code> that indents the code. - * - * @param givenIndentation The indentation added to each line - */ - public ASTWriter(String givenIndentation) { - super(); - this.givenIndentation = givenIndentation; - } - - /** * Generates the source code representing this node. * * @param rootNode Node to write. @@ -75,10 +68,9 @@ public class ASTWriter { * * @see ASTCommenter#getCommentedNodeMap(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit) */ - public String write(IASTNode rootNode, NodeCommentMap commentMap) - throws ProblemRuntimeException { + public String write(IASTNode rootNode, NodeCommentMap commentMap) throws ProblemRuntimeException { ChangeGeneratorWriterVisitor writer = new ChangeGeneratorWriterVisitor( - modificationStore, givenIndentation, null, commentMap); + modificationStore, null, commentMap); if (rootNode != null) { rootNode.accept(writer); } @@ -152,10 +144,35 @@ public class ASTWriter { * @return <code>true</code> if the blank line between the nodes is needed. */ public static boolean requireBlankLineInBetween(IASTNode node1, IASTNode node2) { - if (requiresTrailingBlankLine(node1)) + if (node1 instanceof ICPPASTVisibilityLabel && node2 instanceof ICPPASTVisibilityLabel) { + return true; + } + if (suppressesTrailingBlankLine(node1)) { + return false; + } + if (node1 instanceof IASTPreprocessorIncludeStatement != + node2 instanceof IASTPreprocessorIncludeStatement) { + return true; + } + if (isFunctionDeclaration(node1) != isFunctionDeclaration(node2)) { + return true; + } + if (requiresTrailingBlankLine(node1)) { return true; + } + + return requiresLeadingBlankLine(node2); + } - return !suppressesTrailingBlankLine(node1) && requiresLeadingBlankLine(node2); + private static boolean isFunctionDeclaration(IASTNode node) { + if (!(node instanceof IASTSimpleDeclaration)) { + return false; + } + for (IASTDeclarator declarator : ((IASTSimpleDeclaration) node).getDeclarators()) { + if (declarator instanceof IASTFunctionDeclarator) + return true; + } + return false; } /** diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriterVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriterVisitor.java index 16f9325785f..4ff5321d55a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriterVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/ASTWriterVisitor.java @@ -13,7 +13,7 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.astwriter; -import java.util.ArrayList; +import java.util.List; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTArrayModifier; @@ -29,6 +29,7 @@ import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; @@ -62,28 +63,25 @@ public class ASTWriterVisitor extends ASTVisitor { private boolean spaceNeededBeforeName; { - shouldVisitExpressions = true; - shouldVisitStatements = true; - shouldVisitNames = true; + shouldVisitArrayModifiers = true; + shouldVisitBaseSpecifiers = true; shouldVisitDeclarations = true; - shouldVisitDeclSpecifiers = true; shouldVisitDeclarators = true; - shouldVisitArrayModifiers= true; + shouldVisitDeclSpecifiers = true; + shouldVisitExpressions = true; shouldVisitInitializers = true; - shouldVisitBaseSpecifiers = true; + shouldVisitNames = true; shouldVisitNamespaces = true; - shouldVisitTemplateParameters = true; shouldVisitParameterDeclarations = true; + shouldVisitPointerOperators = true; + shouldVisitStatements = true; + shouldVisitTemplateParameters = true; shouldVisitTranslationUnit = true; + shouldVisitTypeIds = true; } public ASTWriterVisitor(NodeCommentMap commentMap) { - this("", commentMap); //$NON-NLS-1$ - } - - public ASTWriterVisitor(String givenIndentation, NodeCommentMap commentMap) { super(); - scribe.setGivenIndentation(givenIndentation); init(commentMap); this.commentMap = commentMap; this.suppressLeadingBlankLine = true; @@ -123,8 +121,8 @@ public class ASTWriterVisitor extends ASTVisitor { } } - private ArrayList<IASTComment> getLeadingComments(IASTNode node) { - ArrayList<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(node); + private List<IASTComment> getLeadingComments(IASTNode node) { + List<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(node); IASTNodeLocation[] locs = node.getNodeLocations(); if (locs != null && locs.length > 0 && locs[0] instanceof IASTCopyLocation) { IASTCopyLocation copyLoc = (IASTCopyLocation) locs[0]; @@ -240,6 +238,15 @@ public class ASTWriterVisitor extends ASTVisitor { return ASTVisitor.PROCESS_SKIP; } + @Override + public int visit(IASTPointerOperator pointerOperator) { + writeLeadingComments(pointerOperator); + if (!macroHandler.checkisMacroExpansionNode(pointerOperator)) { + declaratorWriter.writePointerOperator(pointerOperator); + } + return ASTVisitor.PROCESS_SKIP; + } + protected IASTName getParameterName(IASTDeclarator declarator) { return declarator.getName(); } @@ -303,6 +310,10 @@ public class ASTWriterVisitor extends ASTVisitor { this.spaceNeededBeforeName = value; } + public Scribe getScribe() { + return scribe; + } + public void newLine() { scribe.newLine(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclSpecWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclSpecWriter.java index ba7fb2e8623..66d25dd9378 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclSpecWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclSpecWriter.java @@ -267,7 +267,7 @@ public class DeclSpecWriter extends NodeWriter { } if (hasFreestandingComments(compDeclSpec)) { - writeFreeStandingComments(compDeclSpec); + writeFreestandingComments(compDeclSpec); } scribe.decrementIndentationLevel(); scribe.print('}'); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java index 41ed66fc643..29e21800a75 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java @@ -98,17 +98,12 @@ public class DeclarationWriter extends NodeWriter { writeVisibilityLabel((ICPPASTVisibilityLabel) declaration); } - if (hasTrailingComments(declaration)) { - writeTrailingComments(declaration, false); - } - if (addNewLine) { - scribe.newLine(); - } + writeTrailingComments(declaration, addNewLine); if (hasFreestandingComments(declaration)) { if (declaration instanceof IASTFunctionDefinition) { scribe.newLine(); } - writeFreeStandingComments(declaration); + writeFreestandingComments(declaration); } } @@ -186,7 +181,7 @@ public class DeclarationWriter extends NodeWriter { scribe.newLine(2); writeDeclarationsInNamespace(namespaceDefinition, namespaceDefinition.getDeclarations()); if (hasFreestandingComments(namespaceDefinition)) { - writeFreeStandingComments(namespaceDefinition); + writeFreestandingComments(namespaceDefinition); } scribe.newLine(); scribe.print('}'); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java index 4a9a8af3852..7d3fdf05c10 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclaratorWriter.java @@ -62,9 +62,7 @@ public class DeclaratorWriter extends NodeWriter { } visitor.setSpaceNeededBeforeName(false); - if (hasTrailingComments(declarator)) { - writeTrailingComments(declarator, false); - } + writeTrailingComments(declarator, false); } protected void writeDefaultDeclarator(IASTDeclarator declarator) { @@ -81,7 +79,7 @@ public class DeclaratorWriter extends NodeWriter { protected void writePointerOperators(IASTDeclarator declarator, IASTPointerOperator[] pointOps) { for (IASTPointerOperator operator : pointOps) { - writePointerOp(operator); + writePointerOperator(operator); } } @@ -194,7 +192,7 @@ public class DeclaratorWriter extends NodeWriter { } } - private void writePointerOp(IASTPointerOperator operator) { + public void writePointerOperator(IASTPointerOperator operator) { if (operator instanceof IASTPointer) { IASTPointer pointOp = (IASTPointer) operator; writePointer(pointOp); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/InitializerWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/InitializerWriter.java index 7adc8fdce86..8b1da40bc37 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/InitializerWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/InitializerWriter.java @@ -51,8 +51,8 @@ public class InitializerWriter extends NodeWriter{ } else if (initializer instanceof ICPPASTConstructorChainInitializer) { writeConstructorChainInitializer((ICPPASTConstructorChainInitializer) initializer); } - if (hasTrailingComments(initializer)) - writeTrailingComments(initializer, false); + + writeTrailingComments(initializer, false); } private void writeEqualsInitializer(IASTEqualsInitializer initializer) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java index 627e8158a5f..aa160a77bdf 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/NodeWriter.java @@ -11,7 +11,7 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.astwriter; -import java.util.ArrayList; +import java.util.List; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTCopyLocation; @@ -83,7 +83,7 @@ public class NodeWriter { } protected void writeTrailingComments(IASTNode node) { - // Default is to write a new line after trailing comments. + // Default is to write a new line after the trailing comments. writeTrailingComments(node, true); } @@ -106,8 +106,8 @@ public class NodeWriter { return !getTrailingComments(node).isEmpty(); } - private ArrayList<IASTComment> getTrailingComments(IASTNode node) { - ArrayList<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(node); + private List<IASTComment> getTrailingComments(IASTNode node) { + List<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(node); IASTNodeLocation[] locs = node.getNodeLocations(); if (locs != null && locs.length > 0 && locs[0] instanceof IASTCopyLocation) { IASTCopyLocation loc = (IASTCopyLocation) locs[0]; @@ -120,8 +120,8 @@ public class NodeWriter { return !getFreestandingComments(node).isEmpty(); } - private ArrayList<IASTComment> getFreestandingComments(IASTNode node) { - ArrayList<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(node); + private List<IASTComment> getFreestandingComments(IASTNode node) { + List<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(node); IASTNodeLocation[] locs = node.getNodeLocations(); if (locs != null && locs.length > 0 && locs[0] instanceof IASTCopyLocation) { IASTCopyLocation loc = (IASTCopyLocation) locs[0]; @@ -130,7 +130,7 @@ public class NodeWriter { return freestandingComments; } - protected void writeFreeStandingComments(IASTNode node) { + protected void writeFreestandingComments(IASTNode node) { for (IASTComment comment : getFreestandingComments(node)) { scribe.print(comment.getComment()); scribe.newLine(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/Scribe.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/Scribe.java index 8bc32d8396d..7f715f5e28f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/Scribe.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/Scribe.java @@ -8,6 +8,7 @@ * * Contributors: * Institute for Software - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.astwriter; @@ -19,42 +20,43 @@ package org.eclipse.cdt.internal.core.dom.rewrite.astwriter; * @author Emanuel Graf IFS */ public class Scribe { - private int indentationLevel = 0; - // Any positive indentation size is good enough since the code is going to be formatted anyway. - private int indentationSize = 4; + // Indentation is not necessary since the code is going to be formatted anyway. + // Preserved because some tests depend on it. + private static final int INDENTATION_SIZE = 4; + private final String newLine = System.getProperty("line.separator"); //$NON-NLS-1$ private StringBuilder buffer = new StringBuilder(); - private boolean isAtLineBeginning = true; - private String newLine = System.getProperty("line.separator"); //$NON-NLS-1$ - private String givenIndentation; + private int indentationLevel = 0; + private boolean isAtBeginningOfLine = true; private boolean skipLineBreaks; private boolean skipSemicolons; + public String getLineSeparator() { + return newLine; + } + public void newLine() { if (!skipLineBreaks) { - isAtLineBeginning = true; - buffer.append(getNewline()); + isAtBeginningOfLine = true; + buffer.append(newLine); } } + public boolean isAtBeginningOfLine() { + return isAtBeginningOfLine; + } + private void indent() { - if (givenIndentation != null) { - buffer.append(givenIndentation); - } - printSpaces(indentationLevel * indentationSize); + printSpaces(indentationLevel * INDENTATION_SIZE); } private void indentIfNewLine() { - if (isAtLineBeginning) { - isAtLineBeginning = false; + if (isAtBeginningOfLine) { + isAtBeginningOfLine = false; indent(); } } - private String getNewline() { - return newLine; - } - public void print(String code) { indentIfNewLine(); buffer.append(code); @@ -171,14 +173,6 @@ public class Scribe { buffer.append(' '); } - public String getGivenIndentation() { - return givenIndentation; - } - - public void setGivenIndentation(String givenIndentation) { - this.givenIndentation = givenIndentation; - } - public void cleanCache() { buffer = new StringBuilder(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/StatementWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/StatementWriter.java index ac8c6f21f37..fd0cf9a59a7 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/StatementWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/StatementWriter.java @@ -154,12 +154,8 @@ public class StatementWriter extends NodeWriter { throw new ProblemRuntimeException((IASTProblemStatement)statement); } - if (hasTrailingComments(statement)) { - writeTrailingComments(statement, newLine); - } else if (newLine) { - scribe.newLine(); - } - + writeTrailingComments(statement, newLine); + return ASTVisitor.PROCESS_SKIP; } @@ -389,7 +385,7 @@ public class StatementWriter extends NodeWriter { } if (hasFreestandingComments(compoundStatement)) { - writeFreeStandingComments(compoundStatement); + writeFreestandingComments(compoundStatement); } if (decrementIndentationLevelOneMore) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ASTModificationHelper.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ASTModificationHelper.java index 3354844d4d1..30fd72f613a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ASTModificationHelper.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ASTModificationHelper.java @@ -15,8 +15,8 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; @@ -100,29 +100,29 @@ public class ASTModificationHelper { private void copyComments(IASTNode newNode, IASTNode oldNode, NodeCommentMap commentMap) { // Attach all the comments that is attached to oldNode to newNode - ArrayList<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(oldNode); + List<IASTComment> leadingComments = commentMap.getLeadingCommentsForNode(oldNode); for (IASTComment comment : leadingComments) { commentMap.addLeadingCommentToNode(newNode, comment); } - ArrayList<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(oldNode); + List<IASTComment> trailingComments = commentMap.getTrailingCommentsForNode(oldNode); for (IASTComment comment : trailingComments) { commentMap.addTrailingCommentToNode(newNode, comment); } - ArrayList<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(oldNode); + List<IASTComment> freestandingComments = commentMap.getFreestandingCommentsForNode(oldNode); for (IASTComment comment : freestandingComments) { commentMap.addFreestandingCommentToNode(newNode, comment); } // Detach comments from oldNode (to avoid memory leak) - HashMap<IASTNode, ArrayList<IASTComment>> leadingMap = commentMap.getLeadingMap(); + Map<IASTNode, List<IASTComment>> leadingMap = commentMap.getLeadingMap(); leadingMap.remove(oldNode); - HashMap<IASTNode, ArrayList<IASTComment>> trailingMap = commentMap.getTrailingMap(); + Map<IASTNode, List<IASTComment>> trailingMap = commentMap.getTrailingMap(); trailingMap.remove(oldNode); - HashMap<IASTNode, ArrayList<IASTComment>> freestandingMap = commentMap.getFreestandingMap(); + Map<IASTNode, List<IASTComment>> freestandingMap = commentMap.getFreestandingMap(); freestandingMap.remove(oldNode); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java index d8d2b173c00..a42d26721ba 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java @@ -15,9 +15,13 @@ package org.eclipse.cdt.internal.core.dom.rewrite.changegenerator; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ToolFactory; @@ -30,15 +34,27 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTExpressionList; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeSelector; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; +import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IASTTypeId; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; import org.eclipse.cdt.core.formatter.CodeFormatter; +import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification; import org.eclipse.cdt.internal.core.dom.rewrite.ASTModification.ModificationKind; @@ -61,11 +77,11 @@ import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextUtilities; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.TextFileChange; -import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; @@ -74,28 +90,31 @@ import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; public class ChangeGenerator extends ASTVisitor { - private final LinkedHashMap<String, Integer> sourceOffsets = new LinkedHashMap<String, Integer>(); - public LinkedHashMap<IASTNode, List<ASTModification>> modificationParent = new LinkedHashMap<IASTNode, List<ASTModification>>(); - private final LinkedHashMap<IFile, MultiTextEdit> changes = new LinkedHashMap<IFile, MultiTextEdit>(); + private final Map<String, Integer> sourceOffsets = new LinkedHashMap<String, Integer>(); + private final Map<IASTNode, Map<ModificationKind, List<ASTModification>>> classifiedModifications = + new HashMap<IASTNode, Map<ModificationKind, List<ASTModification>>>(); + private final Map<IFile, MultiTextEdit> changes = new LinkedHashMap<IFile, MultiTextEdit>(); private CompositeChange change; private final ASTModificationStore modificationStore; private NodeCommentMap commentMap; { - shouldVisitExpressions = true; - shouldVisitStatements = true; + shouldVisitArrayModifiers= true; + shouldVisitBaseSpecifiers = true; shouldVisitNames = true; shouldVisitDeclarations = true; - shouldVisitDeclSpecifiers = true; shouldVisitDeclarators = true; - shouldVisitArrayModifiers= true; + shouldVisitDeclSpecifiers = true; + shouldVisitExpressions = true; shouldVisitInitializers = true; - shouldVisitBaseSpecifiers = true; shouldVisitNamespaces = true; - shouldVisitTemplateParameters = true; shouldVisitParameterDeclarations = true; + shouldVisitPointerOperators = true; + shouldVisitStatements = true; + shouldVisitTemplateParameters = true; shouldVisitTranslationUnit = true; + shouldVisitTypeIds = true; } public ChangeGenerator(ASTModificationStore modificationStore, NodeCommentMap commentMap) { @@ -110,7 +129,7 @@ public class ChangeGenerator extends ASTVisitor { public void generateChange(IASTNode rootNode, ASTVisitor pathProvider) throws ProblemRuntimeException { change = new CompositeChange(Messages.ChangeGenerator_compositeChange); - initParentModList(); + classifyModifications(); rootNode.accept(pathProvider); for (IFile currentFile : changes.keySet()) { MultiTextEdit edit = changes.get(currentFile); @@ -121,145 +140,426 @@ public class ChangeGenerator extends ASTVisitor { } } - private void initParentModList() { + private void classifyModifications() { ASTModificationMap rootModifications = modificationStore.getRootModifications(); - if (rootModifications != null) { - for (IASTNode modifiedNode : rootModifications.getModifiedNodes()) { - List<ASTModification> modificationsForNode = rootModifications.getModificationsForNode(modifiedNode); - IASTNode modifiedNodeParent = determineParentToBeRewritten(modifiedNode, modificationsForNode); - List<ASTModification> list = modificationParent.get(modifiedNodeParent != null ? - modifiedNodeParent : modifiedNode); - if (list != null) { - list.addAll(modificationsForNode); - } else { - List<ASTModification> modifiableList = new ArrayList<ASTModification>(modificationsForNode); - modificationParent.put(modifiedNodeParent != null ? - modifiedNodeParent : modifiedNode, modifiableList); + if (rootModifications == null) + return; + + for (IASTNode node : rootModifications.getModifiedNodes()) { + List<ASTModification> modifications = rootModifications.getModificationsForNode(node); + for (ASTModification modification : modifications) { + Map<ModificationKind, List<ASTModification>> map = classifiedModifications.get(node); + if (map == null) { + map = new TreeMap<ModificationKind, List<ASTModification>>(); + classifiedModifications.put(node, map); } + ModificationKind kind = modification.getKind(); + List<ASTModification> list = map.get(kind); + if (list == null) { + list = new ArrayList<ASTModification>(2); + map.put(kind, list); + } + list.add(modification); } } } - private IASTNode determineParentToBeRewritten(IASTNode modifiedNode, List<ASTModification> modificationsForNode) { - IASTNode modifiedNodeParent = modifiedNode; - for (ASTModification currentModification : modificationsForNode) { - if (currentModification.getKind() == ASTModification.ModificationKind.REPLACE) { - modifiedNodeParent = modifiedNode.getParent(); - break; - } + @Override + public int visit(IASTTranslationUnit tu) { + IASTFileLocation location = tu.getFileLocation(); + sourceOffsets.put(location.getFileName(), Integer.valueOf(location.getNodeOffset())); + return super.visit(tu); + } + + @Override + public int leave(IASTTranslationUnit tu) { + handleAppends(tu); + return super.leave(tu); + } + + @Override + public int visit(IASTDeclaration declaration) { + handleInserts(declaration); + if (requiresRewrite(declaration)) { + handleReplace(declaration); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(declaration); + } + + @Override + public int visit(IASTDeclarator declarator) { + handleInserts(declarator); + if (requiresRewrite(declarator)) { + handleReplace(declarator); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(declarator); + } + + @Override + public int visit(IASTArrayModifier arrayModifier) { + handleInserts(arrayModifier); + if (requiresRewrite(arrayModifier)) { + handleReplace(arrayModifier); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(arrayModifier); + } + + @Override + public int visit(ICPPASTNamespaceDefinition namespaceDefinition) { + handleInserts(namespaceDefinition); + if (requiresRewrite(namespaceDefinition)) { + handleReplace(namespaceDefinition); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(namespaceDefinition); + } + + @Override + public int leave(ICPPASTNamespaceDefinition namespaceDefinition) { + if (!requiresRewrite(namespaceDefinition)) { + handleAppends(namespaceDefinition); + } + return super.leave(namespaceDefinition); + } + + @Override + public int visit(IASTDeclSpecifier declSpec) { + handleInserts(declSpec); + if (requiresRewrite(declSpec)) { + handleReplace(declSpec); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(declSpec); + } + + @Override + public int leave(IASTDeclSpecifier declSpec) { + if (!requiresRewrite(declSpec)) { + handleAppends(declSpec); + } + return super.leave(declSpec); + } + + @Override + public int visit(IASTExpression expression) { + handleInserts(expression); + if (requiresRewrite(expression)) { + handleReplace(expression); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(expression); + } + + @Override + public int visit(IASTInitializer initializer) { + handleInserts(initializer); + if (requiresRewrite(initializer)) { + handleReplace(initializer); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(initializer); + } + + @Override + public int visit(IASTName name) { + handleInserts(name); + if (requiresRewrite(name)) { + handleReplace(name); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(name); + } + + @Override + public int visit(IASTParameterDeclaration parameterDeclaration) { + handleInserts(parameterDeclaration); + if (requiresRewrite(parameterDeclaration)) { + handleReplace(parameterDeclaration); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(parameterDeclaration); + } + + @Override + public int visit(IASTPointerOperator pointerOperator) { + handleInserts(pointerOperator); + if (requiresRewrite(pointerOperator)) { + handleReplace(pointerOperator); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(pointerOperator); + } + + @Override + public int visit(IASTTypeId typeId) { + handleInserts(typeId); + if (requiresRewrite(typeId)) { + handleReplace(typeId); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(typeId); + } + + @Override + public int visit(IASTStatement statement) { + handleInserts(statement); + if (requiresRewrite(statement)) { + handleReplace(statement); + return ASTVisitor.PROCESS_SKIP; + } + return super.visit(statement); + } + + @Override + public int leave(IASTStatement statement) { + if (!requiresRewrite(statement)) { + handleAppends(statement); } - modifiedNodeParent = modifiedNodeParent != null ? modifiedNodeParent : modifiedNode; - return modifiedNodeParent; + return super.leave(statement); } /** * Applies the C++ code formatter to the code affected by refactoring. * - * @param edit The text edit produced by refactoring. + * @param multiEdit The text edit produced by refactoring. * @param file The file being modified. * @return The text edit containing formatted refactoring changes, or the original text edit * in case of errors. */ - private MultiTextEdit formatChangedCode(MultiTextEdit edit, IFile file) { + private MultiTextEdit formatChangedCode(MultiTextEdit multiEdit, IFile file) { String code; try { code = FileContentHelper.getContent(file, 0); } catch (IOException e) { CCorePlugin.log(e); - return edit; + return multiEdit; } catch (CoreException e) { CCorePlugin.log(e); - return edit; + return multiEdit; } IDocument document = new Document(code); try { - TextEdit tempEdit = edit.copy(); - tempEdit.apply(document, TextEdit.UPDATE_REGIONS); - TextEdit[] edits = tempEdit.getChildren(); - IRegion[] regions = new IRegion[edits.length]; + // Apply refactoring changes to a temporary document. + TextEdit edit = multiEdit.copy(); + edit.apply(document, TextEdit.UPDATE_REGIONS); + + // Expand regions affected by the changes to cover complete lines. We calculate two + // sets of regions, reflecting the state of the document before and after + // the refactoring changes. + TextEdit[] edits = edit.getChildren(); + TextEdit[] originalEdits = multiEdit.getChildren(); + IRegion[] regionsAfter = new IRegion[edits.length]; + IRegion[] regionsBefore = new IRegion[edits.length]; + int numRegions = 0; + int prevEnd = -1; for (int i = 0; i < edits.length; i++) { - regions[i] = edits[i].getRegion(); + edit = edits[i]; + int offset = edit.getOffset(); + int end = offset + edit.getLength(); + int newOffset = document.getLineInformationOfOffset(offset).getOffset(); + int newEnd = endOffset(document.getLineInformationOfOffset(end)); + edit = originalEdits[i]; + int offsetBefore = edit.getOffset(); + int newOffsetBefore = newOffset + offsetBefore - offset; + int newEndBefore = newEnd + offsetBefore + edit.getLength() - end; + if (newOffset <= prevEnd) { + numRegions--; + newOffset = regionsAfter[numRegions].getOffset(); + newOffsetBefore = regionsBefore[numRegions].getOffset(); + } + prevEnd = newEnd; + regionsAfter[numRegions] = new Region(newOffset, newEnd - newOffset); + regionsBefore[numRegions] = new Region(newOffsetBefore, newEndBefore - newOffsetBefore); + numRegions++; + } + + if (numRegions < regionsAfter.length) { + regionsAfter = Arrays.copyOf(regionsAfter, numRegions); + regionsBefore = Arrays.copyOf(regionsBefore, numRegions); } + + // Calculate formatting changes for the regions after the refactoring changes. ICProject project = CCorePlugin.getDefault().getCoreModel().create(file.getProject()); Map<String, String> options = project.getOptions(true); + // Allow all comments to be indented. + options.put(DefaultCodeFormatterConstants.FORMATTER_COMMENT_NEVER_INDENT_LINE_COMMENTS_ON_FIRST_COLUMN, + DefaultCodeFormatterConstants.FALSE); CodeFormatter formatter = ToolFactory.createCodeFormatter(options); code = document.get(); TextEdit[] formatEdits = formatter.format(CCodeFormatter.K_TRANSLATION_UNIT, code, - regions, TextUtilities.getDefaultLineDelimiter(document)); + regionsAfter, TextUtilities.getDefaultLineDelimiter(document)); + + // For each of the regions we apply formatting changes and create a ReplaceEdit using + // the region before the refactoring changes and the text after the formatting changes. MultiTextEdit resultEdit = new MultiTextEdit(); - edits = edit.getChildren(); - for (int i = 0; i < edits.length; i++) { - IRegion region = regions[i]; + for (int i = 0; i < regionsAfter.length; i++) { + IRegion region = regionsAfter[i]; int offset = region.getOffset(); - TextEdit formatEdit = formatEdits[i]; - formatEdit.moveTree(-offset); + edit = formatEdits[i]; + edit.moveTree(-offset); document = new Document(code.substring(offset, offset + region.getLength())); - formatEdit.apply(document, TextEdit.NONE); - TextEdit textEdit = edits[i]; - resultEdit.addChild( - new ReplaceEdit(textEdit.getOffset(), textEdit.getLength(), document.get())); + edit.apply(document, TextEdit.NONE); + region = regionsBefore[i]; + edit = new ReplaceEdit(region.getOffset(), region.getLength(), document.get()); + resultEdit.addChild(edit); } return resultEdit; } catch (MalformedTreeException e) { CCorePlugin.log(e); - return edit; + return multiEdit; } catch (BadLocationException e) { CCorePlugin.log(e); - return edit; + return multiEdit; } } - @Override - public int visit(IASTTranslationUnit translationUnit) { - if (hasChangedChild(translationUnit)) { - synthTreatment(translationUnit); - } - IASTFileLocation location = translationUnit.getFileLocation(); - sourceOffsets.put(location.getFileName(), Integer.valueOf(location.getNodeOffset())); - return super.visit(translationUnit); + private int endOffset(IRegion region) { + return region.getOffset() + region.getLength(); } - @Override - public int leave(IASTTranslationUnit tu) { - return super.leave(tu); + private int endOffset(IASTFileLocation nodeLocation) { + return nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); } - @Override - public int visit(IASTDeclaration declaration) { - if (hasChangedChild(declaration)) { - synthTreatment(declaration); - return ASTVisitor.PROCESS_SKIP; + private int endOffset(IASTNode node) { + return endOffset(node.getFileLocation()); + } + + private int offset(IASTNode node) { + return node.getFileLocation().getNodeOffset(); + } + + private void handleInserts(IASTNode anchorNode) { + List<ASTModification> modifications = getModifications(anchorNode, ModificationKind.INSERT_BEFORE); + if (modifications.isEmpty()) + return; + ChangeGeneratorWriterVisitor writer = + new ChangeGeneratorWriterVisitor(modificationStore, commentMap); + IASTNode newNode = null; + for (ASTModification modification : modifications) { + boolean first = newNode == null; + newNode = modification.getNewNode(); + if (first) { + IASTNode prevNode = getPreviousSiblingOrPreprocessorNode(anchorNode); + if (prevNode != null) { + if (ASTWriter.requireBlankLineInBetween(prevNode, newNode)) { + writer.newLine(); + } + } else if (anchorNode.getParent() instanceof ICPPASTNamespaceDefinition) { + writer.newLine(); + } + } + newNode.accept(writer); + if (getContainingNodeList(anchorNode) != null) { + writer.getScribe().print(", "); //$NON-NLS-1$ + } } - return super.visit(declaration); + if (ASTWriter.requireBlankLineInBetween(newNode, anchorNode)) { + writer.newLine(); + } + int insertPos = getOffsetIncludingComments(anchorNode); + int length = 0; + if (writer.getScribe().isAtBeginningOfLine()) { + String tuCode = anchorNode.getTranslationUnit().getRawSignature(); + insertPos = skipPrecedingWhitespace(tuCode, insertPos); + length = insertPos; + insertPos = skipPrecedingBlankLines(tuCode, insertPos); + length -= insertPos; + } + String code = writer.toString(); + ReplaceEdit edit = new ReplaceEdit(insertPos, length, code); + IFile file = FileHelper.getFileFromNode(anchorNode); + MultiTextEdit parentEdit = getEdit(anchorNode, file); + parentEdit.addChild(edit); + sourceOffsets.put(file.getName(), Integer.valueOf(edit.getOffset())); } - private void synthTreatment(IASTNode synthNode) { + private void handleReplace(IASTNode node) { + List<ASTModification> modifications = getModifications(node, ModificationKind.REPLACE); + String source = node.getTranslationUnit().getRawSignature(); + TextEdit edit; ChangeGeneratorWriterVisitor writer = new ChangeGeneratorWriterVisitor(modificationStore, commentMap); - synthNode.accept(writer); - String synthSource = writer.toString(); - createChange(synthNode, synthSource); + if (modifications.size() == 1 && modifications.get(0).getNewNode() == null) { + int offset = getOffsetIncludingComments(node); + int endOffset = getEndOffsetIncludingComments(node); + offset = skipPrecedingBlankLines(source, offset); + endOffset = skipTrailingBlankLines(source, endOffset); + IASTNode[] siblingsList = getContainingNodeList(node); + if (siblingsList != null) { + if (siblingsList.length > 1) { + if (node == siblingsList[0]) { + endOffset = skipToTrailingDelimiter(source, ',', endOffset); + } else { + offset = skipToPrecedingDelimiter(source, ',', offset); + } + } else if (node.getPropertyInParent() == ICPPASTFunctionDefinition.MEMBER_INITIALIZER) { + offset = skipToPrecedingDelimiter(source, ':', offset); + } + } + IASTNode prevNode = getPreviousSiblingOrPreprocessorNode(node); + IASTNode nextNode = getNextSiblingOrPreprocessorNode(node); + if (prevNode != null && nextNode != null) { + if (ASTWriter.requireBlankLineInBetween(prevNode, nextNode)) { + writer.newLine(); + } + } else if (node.getParent() instanceof ICPPASTNamespaceDefinition) { + writer.newLine(); + } + String code = writer.toString(); + edit = new ReplaceEdit(offset, endOffset - offset, code); + } else { + node.accept(writer); + String code = writer.toString(); + IASTFileLocation location = node.getFileLocation(); + int offset = location.getNodeOffset(); + int endOffset = offset + location.getNodeLength(); + if (node instanceof IASTStatement || node instanceof IASTDeclaration) { + // Include trailing comments in the area to be replaced. + endOffset = Math.max(endOffset, getEndOffsetIncludingTrailingComments(node)); + } + String lineSeparator = writer.getScribe().getLineSeparator(); + if (code.endsWith(lineSeparator)) { + code = code.substring(0, code.length() - lineSeparator.length()); + } + edit = new ReplaceEdit(offset, endOffset - offset, code); + } + IFile file = FileHelper.getFileFromNode(node); + MultiTextEdit parentEdit = getEdit(node, file); + parentEdit.addChild(edit); - IASTFileLocation fileLocation = synthNode.getFileLocation(); + IASTFileLocation fileLocation = node.getFileLocation(); int newOffset = fileLocation.getNodeOffset() + fileLocation.getNodeLength(); sourceOffsets.put(fileLocation.getFileName(), Integer.valueOf(newOffset)); } private void handleAppends(IASTNode node) { + List<ASTModification> modifications = getModifications(node, ModificationKind.APPEND_CHILD); + if (modifications.isEmpty()) + return; ChangeGeneratorWriterVisitor writer = new ChangeGeneratorWriterVisitor(modificationStore, commentMap); - List<ASTModification> modifications = modificationParent.get(node); ReplaceEdit anchor = getAppendAnchor(node); Assert.isNotNull(anchor); IASTNode precedingNode = getLastNodeBeforeAppendPoint(node); - if (precedingNode != null && - ASTWriter.requireBlankLineInBetween(precedingNode, modifications.get(0).getNewNode())) { - writer.newLine(); - } for (ASTModification modification : modifications) { IASTNode newNode = modification.getNewNode(); + if (precedingNode != null) { + if (ASTWriter.requireBlankLineInBetween(precedingNode, newNode)) { + writer.newLine(); + } + } else if (node instanceof ICPPASTNamespaceDefinition) { + writer.newLine(); + } + precedingNode = null; newNode.accept(writer); } + if (node instanceof ICPPASTNamespaceDefinition) { + writer.newLine(); + } String code = writer.toString(); IFile file = FileHelper.getFileFromNode(node); MultiTextEdit parentEdit = getEdit(node, file); @@ -271,29 +571,217 @@ public class ChangeGenerator extends ASTVisitor { sourceOffsets.put(fileLocation.getFileName(), Integer.valueOf(newOffset)); } + private void handleAppends(IASTTranslationUnit tu) { + ASTWriter synthWriter = new ASTWriter(); + synthWriter.setModificationStore(modificationStore); + + for (ASTModification modification : getModifications(tu, ModificationKind.APPEND_CHILD)) { + IASTNode targetNode = modification.getTargetNode(); + IASTFileLocation targetLocation = targetNode.getFileLocation(); + String currentFile = targetLocation.getFileName(); + IPath implPath = new Path(currentFile); + IFile relevantFile= ResourceLookup.selectFileForLocation(implPath, null); + if (relevantFile == null || !relevantFile.exists()) { // If not in workspace or local file system + throw new UnhandledASTModificationException(modification); + } + MultiTextEdit edit; + if (changes.containsKey(relevantFile)) { + edit = changes.get(relevantFile); + } else { + edit = new MultiTextEdit(); + changes.put(relevantFile, edit); + } + String code = synthWriter.write(modification.getNewNode(), commentMap); + + if (targetNode instanceof IASTTranslationUnit && + ((IASTTranslationUnit) targetNode).getDeclarations().length > 0) { + IASTTranslationUnit targetTu = (IASTTranslationUnit) targetNode; + IASTDeclaration lastDecl = targetTu.getDeclarations()[targetTu.getDeclarations().length - 1]; + targetLocation = lastDecl.getFileLocation(); + } + String lineDelimiter = FileHelper.determineLineDelimiter( + FileHelper.getFileFromNode(targetNode)); + edit.addChild(new InsertEdit(endOffset(targetLocation), lineDelimiter + lineDelimiter + code)); + } + } + + /** + * Returns the list of nodes the given node is part of, for example function parameters if + * the node is a parameter. + * + * @param node the node possibly belonging to a list. + * @return the list of nodes containing the given node, or <code>null</code> if the node + * does not belong to a list + */ + private IASTNode[] getContainingNodeList(IASTNode node) { + if (node.getPropertyInParent() == IASTStandardFunctionDeclarator.FUNCTION_PARAMETER) { + return ((IASTStandardFunctionDeclarator) node.getParent()).getParameters(); + } else if (node.getPropertyInParent() == IASTExpressionList.NESTED_EXPRESSION) { + return ((IASTExpressionList) node.getParent()).getExpressions(); + } else if (node.getPropertyInParent() == ICPPASTFunctionDefinition.MEMBER_INITIALIZER) { + return ((ICPPASTFunctionDefinition) node.getParent()).getMemberInitializers(); + } else if (node.getPropertyInParent() == ICPPASTFunctionDeclarator.EXCEPTION_TYPEID) { + return ((ICPPASTFunctionDeclarator) node.getParent()).getExceptionSpecification(); + } + + return null; + } + private IASTNode getLastNodeBeforeAppendPoint(IASTNode node) { IASTNode[] children; - if (node instanceof IASTCompositeTypeSpecifier) { + if (node instanceof ICPPASTNamespaceDefinition) { + children = ((ICPPASTNamespaceDefinition) node).getDeclarations(true); + } else if (node instanceof IASTCompositeTypeSpecifier) { children = ((IASTCompositeTypeSpecifier) node).getDeclarations(true); } else { children = node.getChildren(); } - return children.length > 0 ? children[children.length - 1] : null; + for (int i = children.length; --i >= 0;) { + IASTNode child = getReplacementNode(children[i]); + if (child != null) + return child; + } + return null; } - private boolean isAppendable(Iterable<ASTModification> modifications) { - for (ASTModification modification : modifications) { - if (!isAppendable(modification)) + private IASTNode getReplacementNode(IASTNode node) { + List<ASTModification> modifications = getModifications(node, ModificationKind.REPLACE); + if (!modifications.isEmpty()) { + node = modifications.get(modifications.size() - 1).getNewNode(); + } + return node; + } + + private IASTNode getPreviousSiblingNode(IASTNode node) { + IASTNode parent = node.getParent(); + IASTNode[] siblings; + if (parent instanceof ICPPASTNamespaceDefinition) { + siblings = ((ICPPASTNamespaceDefinition) parent).getDeclarations(true); + } else if (parent instanceof IASTCompositeTypeSpecifier) { + siblings = ((IASTCompositeTypeSpecifier) parent).getDeclarations(true); + } else { + siblings = parent.getChildren(); + } + boolean beforeNode = false; + for (int i = siblings.length; --i >= 0;) { + IASTNode sibling = siblings[i]; + if (sibling == node) { + beforeNode = true; + } else if (beforeNode) { + sibling = getReplacementNode(sibling); + if (sibling != null) + return sibling; + } + } + return null; + } + + private IASTNode getPreviousSiblingOrPreprocessorNode(IASTNode node) { + int offset = offset(node); + IASTTranslationUnit ast = node.getTranslationUnit(); + IASTPreprocessorStatement[] preprocessorStatements = ast.getAllPreprocessorStatements(); + int low = 0; + int high = preprocessorStatements.length; + while (low < high) { + int mid = (low + high) / 2; + IASTNode statement = preprocessorStatements[mid]; + if (statement.isPartOfTranslationUnitFile() && endOffset(statement) > offset) { + high = mid; + } else { + low = mid + 1; + } + } + low--; + if (low >= 0) { + IASTNode statement = preprocessorStatements[low]; + if (statement.isPartOfTranslationUnitFile()) { + int endOffset = endOffset(statement); + if (!doesRegionContainNode(ast, endOffset, offset - endOffset)) { + return statement; + } + } + } + + return getPreviousSiblingNode(node); + } + + private IASTNode getNextSiblingNode(IASTNode node) { + IASTNode parent = node.getParent(); + IASTNode[] siblings; + if (parent instanceof ICPPASTNamespaceDefinition) { + siblings = ((ICPPASTNamespaceDefinition) parent).getDeclarations(true); + } else if (parent instanceof IASTCompositeTypeSpecifier) { + siblings = ((IASTCompositeTypeSpecifier) parent).getDeclarations(true); + } else { + siblings = parent.getChildren(); + } + boolean beforeNode = false; + for (int i = 0; i < siblings.length; i++) { + IASTNode sibling = siblings[i]; + if (sibling == node) { + beforeNode = true; + } else if (beforeNode) { + sibling = getReplacementNode(sibling); + if (sibling != null) + return sibling; + } + } + return null; + } + + private IASTNode getNextSiblingOrPreprocessorNode(IASTNode node) { + int endOffset = endOffset(node); + IASTTranslationUnit ast = node.getTranslationUnit(); + IASTPreprocessorStatement[] preprocessorStatements = ast.getAllPreprocessorStatements(); + int low = 0; + int high = preprocessorStatements.length; + while (low < high) { + int mid = (low + high) / 2; + IASTNode statement = preprocessorStatements[mid]; + if (statement.isPartOfTranslationUnitFile() && offset(statement) > endOffset) { + high = mid; + } else { + low = mid + 1; + } + } + if (high < preprocessorStatements.length) { + IASTNode statement = preprocessorStatements[high]; + if (statement.isPartOfTranslationUnitFile()) { + int offset = offset(statement); + if (!doesRegionContainNode(ast, endOffset, offset - endOffset)) { + return statement; + } + } + } + + return getNextSiblingNode(node); + } + + /** + * Checks if a given region contains at least a piece of a node after rewrite. + */ + private boolean doesRegionContainNode(IASTTranslationUnit ast, int offset, int length) { + IASTNodeSelector nodeSelector = ast.getNodeSelector(ast.getFilePath()); + while (length > 0) { + IASTNode node = nodeSelector.findFirstContainedNode(offset, length - 1); + if (node == null) return false; + if (!isNodeRemoved(node)) + return true; + int oldOffset = offset; + offset = endOffset(node); + length -= offset - oldOffset; } - return true; + return false; } - private boolean isAppendable(ASTModification modification) { - if (modification.getKind() != ModificationKind.APPEND_CHILD) - return false; - IASTNode node = modification.getNewNode(); - return node instanceof IASTDeclaration || node instanceof IASTStatement; + private boolean isNodeRemoved(IASTNode node) { + do { + if (getReplacementNode(node) == null) + return true; + } while ((node = node.getParent()) != null); + + return false; } /** @@ -310,93 +798,148 @@ public class ChangeGenerator extends ASTVisitor { node instanceof ICPPASTNamespaceDefinition)) { return null; } - IFile file = FileHelper.getFileFromNode(node); - String code = originalCodeOfNode(node, file); + String code = node.getRawSignature(); IASTFileLocation location = node.getFileLocation(); int pos = location.getNodeOffset() + location.getNodeLength(); int len = code.endsWith("}") ? 1 : 0; //$NON-NLS-1$ - int startOfLine = skipPrecedingBlankLines(code, code.length() - len); - if (startOfLine < 0) { + int insertPos = code.length() - len; + int startOfLine = skipPrecedingBlankLines(code, insertPos); + if (startOfLine == insertPos) { // Include the closing brace in the region that will be reformatted. - return new ReplaceEdit(pos - len, len, code.substring(code.length() - len)); + return new ReplaceEdit(pos - len, len, code.substring(insertPos)); } - return new ReplaceEdit(location.getNodeOffset() + startOfLine, 0, ""); //$NON-NLS-1$ + return new ReplaceEdit(location.getNodeOffset() + startOfLine, insertPos - startOfLine, ""); //$NON-NLS-1$ } /** - * Skips blank lines preceding the given position. - * @param text the text to scan - * @param pos the position after that blank lines. - * @return the beginning of the first blank line, or -1 if the beginning of the line - * corresponding to the given position contains non-whitespace characters. + * Skips whitespace between the beginning of the line and the given position. + * + * @param text The text to scan. + * @param startPos The start position. + * @return The beginning of the line containing the start position, if there are no + * non-whitespace characters between the beginning of the line and the start position. + * Otherwise returns the start position. */ - private int skipPrecedingBlankLines(String text, int pos) { - int lineStart = -1; - while (--pos >= 0) { + private int skipPrecedingWhitespace(String text, int startPos) { + for (int pos = startPos; --pos >= 0; ) { char c = text.charAt(pos); if (c == '\n') { - lineStart = pos + 1; + return pos + 1; } else if (!Character.isWhitespace(c)) { - break; + return startPos; } } - return lineStart; + return 0; } - private void synthTreatment(IASTTranslationUnit synthTU) { - ASTWriter synthWriter = new ASTWriter(); - synthWriter.setModificationStore(modificationStore); + /** + * Skips whitespace between the beginning of the line and the given position and blank lines + * above that. + * + * @param text The text to scan. + * @param startPos The start position. + * @return The beginning of the first blank line preceding the start position, + * or beginning of the current line, if there are no non-whitespace characters between + * the beginning of the line and the start position. + * Otherwise returns the start position. + */ + private int skipPrecedingBlankLines(String text, int startPos) { + for (int pos = startPos; --pos >= 0;) { + char c = text.charAt(pos); + if (c == '\n') { + startPos = pos + 1; + } else if (!Character.isWhitespace(c)) { + return startPos; + } + } + return 0; + } - for (ASTModification modification : modificationParent.get(synthTU)) { - IASTNode targetNode = modification.getTargetNode(); - IASTFileLocation targetLocation = targetNode.getFileLocation(); - String currentFile = targetLocation.getFileName(); - IPath implPath = new Path(currentFile); - IFile relevantFile= ResourceLookup.selectFileForLocation(implPath, null); - if (relevantFile == null || !relevantFile.exists()) { // If not in workspace or local file system - throw new UnhandledASTModificationException(modification); + /** + * Skips whitespace between the given position and the end of the line. + * + * @param text The text to scan. + * @param startPos The start position. + * @return The end of the line containing the start position, if there are no non-whitespace + * characters between the start position and the end of the line. + * Otherwise returns the start position. + */ + private int skipTrailingWhitespace(String text, int startPos) { + for (int pos = startPos; pos < text.length(); pos++) { + char c = text.charAt(pos); + if (c == '\n') { + return pos + 1; + } else if (!Character.isWhitespace(c)) { + return startPos; } - MultiTextEdit edit; - if (changes.containsKey(relevantFile)) { - edit = changes.get(relevantFile); - } else { - edit = new MultiTextEdit(); - changes.put(relevantFile, edit); + } + return text.length(); + } + + /** + * Skips whitespace between the given position and the end of the line and blank lines + * below that. + * + * @param text The text to scan. + * @param startPos The start position. + * @return The beginning of the first non-blank line following the start position, if there are + * no non-whitespace characters between the start position and the end of the line. + * Otherwise returns the start position. + */ + private int skipTrailingBlankLines(String text, int startPos) { + for (int pos = startPos; pos < text.length(); pos++) { + char c = text.charAt(pos); + if (c == '\n') { + startPos = pos + 1; + } else if (!Character.isWhitespace(c)) { + return startPos; } - String newNodeCode = synthWriter.write(modification.getNewNode(), commentMap); - - switch (modification.getKind()) { - case REPLACE: - edit.addChild(new ReplaceEdit(targetLocation.getNodeOffset(), - targetLocation.getNodeLength(), newNodeCode)); - break; - case INSERT_BEFORE: - if (ASTWriter.requireBlankLineInBetween(modification.getNewNode(), targetNode)) { - newNodeCode = newNodeCode + "\n"; //$NON-NLS-1$ - } - edit.addChild(new InsertEdit(getOffsetIncludingComments(targetNode), newNodeCode)); - break; - case APPEND_CHILD: - if (targetNode instanceof IASTTranslationUnit && - ((IASTTranslationUnit) targetNode).getDeclarations().length > 0) { - IASTTranslationUnit tu = (IASTTranslationUnit) targetNode; - IASTDeclaration lastDecl = tu.getDeclarations()[tu.getDeclarations().length - 1]; - targetLocation = lastDecl.getFileLocation(); - } - String lineDelimiter = FileHelper.determineLineDelimiter( - FileHelper.getFileFromNode(targetNode)); - edit.addChild(new InsertEdit(targetLocation.getNodeOffset() + targetLocation.getNodeLength(), - lineDelimiter + lineDelimiter + newNodeCode)); - break; + } + return text.length(); + } + + /** + * Skips whitespace to the left of the given position until the given delimiter character + * is found. + * + * @param text The text to scan. + * @param delimiter the delimiter to find + * @param startPos The start position. + * @return The position of the given delimiter, or the start position if a non-whitespace + * character is encountered before the given delimiter. + */ + private int skipToPrecedingDelimiter(String text, char delimiter, int startPos) { + for (int pos = startPos; --pos >= 0; ) { + char c = text.charAt(pos); + if (c == delimiter) { + return pos; + } else if (!Character.isWhitespace(c)) { + return startPos; } } + return startPos; } - private void createChange(IASTNode synthNode, String synthSource) { - IFile relevantFile = FileHelper.getFileFromNode(synthNode); - String originalCode = originalCodeOfNode(synthNode, relevantFile); - CodeComparer codeComparer = new CodeComparer(originalCode, synthSource); - codeComparer.createChange(getEdit(synthNode, relevantFile), synthNode); + /** + * Skips whitespace to the right of the given position until the given delimiter character + * is found. + * + * @param text The text to scan. + * @param delimiter the delimiter to find + * @param startPos The start position. + * @return The position after the given delimiter, or the start position if a non-whitespace + * character is encountered before the given delimiter. + */ + private int skipToTrailingDelimiter(String text, char delimiter, int startPos) { + for (int pos = startPos; pos < text.length(); pos++) { + char c = text.charAt(pos); + if (c == delimiter) { + return pos + 1; + } else if (!Character.isWhitespace(c)) { + return startPos; + } + } + return startPos; } private MultiTextEdit getEdit(IASTNode modifiedNode, IFile file) { @@ -406,366 +949,108 @@ public class ChangeGenerator extends ASTVisitor { changes.put(file, edit); } TextEditGroup editGroup = new TextEditGroup(Messages.ChangeGenerator_group); - for (ASTModification currentModification : modificationParent.get(modifiedNode)) { - if (currentModification.getAssociatedEditGroup() != null) { - editGroup = currentModification.getAssociatedEditGroup(); - edit.addChildren(editGroup.getTextEdits()); - break; + for (List<ASTModification> modifications : getModifications(modifiedNode).values()) { + for (ASTModification modification : modifications) { + if (modification.getAssociatedEditGroup() != null) { + editGroup = modification.getAssociatedEditGroup(); + edit.addChildren(editGroup.getTextEdits()); + return edit; + } } } return edit; } - private String originalCodeOfNode(IASTNode node, IFile sourceFile) { - int nodeOffset = getOffsetIncludingComments(node); - int nodeLength = getNodeLengthIncludingComments(node); - return FileContentHelper.getContent(sourceFile, nodeOffset, nodeLength); - } - - private int getNodeLengthIncludingComments(IASTNode node) { + private int getOffsetIncludingComments(IASTNode node) { int nodeOffset = node.getFileLocation().getNodeOffset(); - int nodeLength = node.getFileLocation().getNodeLength(); - ArrayList<IASTComment> comments = commentMap.getAllCommentsForNode(node); + List<IASTComment> comments = commentMap.getAllCommentsForNode(node); if (!comments.isEmpty()) { int startOffset = nodeOffset; - int endOffset = nodeOffset + nodeLength; for (IASTComment comment : comments) { IASTFileLocation commentLocation = comment.getFileLocation(); if (commentLocation.getNodeOffset() < startOffset) { startOffset = commentLocation.getNodeOffset(); } - if (commentLocation.getNodeOffset() + commentLocation.getNodeLength() >= endOffset) { - endOffset = commentLocation.getNodeOffset() + commentLocation.getNodeLength(); - } } - nodeLength = endOffset - startOffset; + nodeOffset = startOffset; } - return nodeLength; + return nodeOffset; } - private int getOffsetIncludingComments(IASTNode node) { - int nodeOffset = node.getFileLocation().getNodeOffset(); + private int getEndOffsetIncludingComments(IASTNode node) { + IASTFileLocation nodeLocation = node.getFileLocation(); + int endOffset = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); - ArrayList<IASTComment> comments = commentMap.getAllCommentsForNode(node); + List<IASTComment> comments = commentMap.getAllCommentsForNode(node); if (!comments.isEmpty()) { - int startOffset = nodeOffset; for (IASTComment comment : comments) { - IASTFileLocation commentLocation = comment.getFileLocation(); - if (commentLocation.getNodeOffset() < startOffset) { - startOffset = commentLocation.getNodeOffset(); + int commentEndOffset = endOffset(comment.getFileLocation()); + if (commentEndOffset >= endOffset) { + endOffset = commentEndOffset; } } - nodeOffset = startOffset; } - return nodeOffset; + return endOffset; } - private boolean hasChangedChild(IASTNode node) { - return modificationParent.containsKey(node); - } + private int getEndOffsetIncludingTrailingComments(IASTNode node) { + IASTFileLocation nodeLocation = node.getFileLocation(); + int endOffset = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); - private boolean hasAppendsOnly(IASTNode node) { - List<ASTModification> modifications = modificationParent.get(node); - if (modifications == null) - return false; - return isAppendable(modifications); - } - - @Override - public int visit(IASTDeclarator declarator) { - if (hasChangedChild(declarator)) { - synthTreatment(declarator); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(declarator); - } - - @Override - public int visit(IASTArrayModifier mod) { - if (hasChangedChild(mod)) { - synthTreatment(mod); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(mod); - } - - @Override - public int visit(ICPPASTNamespaceDefinition namespaceDefinition) { - if (hasChangedChild(namespaceDefinition) && !hasAppendsOnly(namespaceDefinition)) { - synthTreatment(namespaceDefinition); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(namespaceDefinition); - } - - @Override - public int leave(ICPPASTNamespaceDefinition namespaceDefinition) { - if (hasAppendsOnly(namespaceDefinition)) { - handleAppends(namespaceDefinition); - } - return super.leave(namespaceDefinition); - } - - @Override - public int visit(IASTDeclSpecifier declSpec) { - if (hasChangedChild(declSpec) && !hasAppendsOnly(declSpec)) { - synthTreatment(declSpec); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(declSpec); - } - - @Override - public int leave(IASTDeclSpecifier declSpec) { - if (hasAppendsOnly(declSpec)) { - handleAppends(declSpec); - } - return super.leave(declSpec); - } - - @Override - public int visit(IASTExpression expression) { - if (hasChangedChild(expression)) { - synthTreatment(expression); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(expression); - } - - @Override - public int visit(IASTInitializer initializer) { - if (hasChangedChild(initializer)) { - synthTreatment(initializer); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(initializer); - } - - @Override - public int visit(IASTName name) { - if (hasChangedChild(name)) { - synthTreatment(name); - return ASTVisitor.PROCESS_SKIP; + List<IASTComment> comments = commentMap.getTrailingCommentsForNode(node); + if (!comments.isEmpty()) { + for (IASTComment comment : comments) { + int commentEndOffset = endOffset(comment.getFileLocation()); + if (commentEndOffset >= endOffset) { + endOffset = commentEndOffset; + } + } } - return super.visit(name); - } - - @Override - public int visit(IASTParameterDeclaration parameterDeclaration) { - if (hasChangedChild(parameterDeclaration)) { - synthTreatment(parameterDeclaration); - return ASTVisitor.PROCESS_SKIP; + // TODO(sprigogin): Remove when comments are always assigned to the outermost nodes. + if (node instanceof ICPPASTFunctionWithTryBlock) { + ICPPASTCatchHandler[] catchHandlers = ((ICPPASTFunctionWithTryBlock) node).getCatchHandlers(); + if (catchHandlers.length > 0) { + endOffset = Math.max(endOffset, + getEndOffsetIncludingTrailingComments(catchHandlers[catchHandlers.length - 1])); + } + } else if (node instanceof IASTFunctionDefinition) { + endOffset = Math.max(endOffset, + getEndOffsetIncludingTrailingComments(((IASTFunctionDefinition) node).getBody())); } - return super.visit(parameterDeclaration); + return endOffset; } - @Override - public int visit(IASTStatement statement) { - if (hasChangedChild(statement) && !hasAppendsOnly(statement)) { - synthTreatment(statement); - return ASTVisitor.PROCESS_SKIP; - } - return super.visit(statement); + private Map<ModificationKind, List<ASTModification>> getModifications(IASTNode node) { + Map<ModificationKind, List<ASTModification>> modifications = classifiedModifications.get(node); + if (modifications == null) + return Collections.emptyMap(); + return modifications; } - @Override - public int leave(IASTStatement statement) { - if (hasAppendsOnly(statement)) { - handleAppends(statement); - } - return super.leave(statement); + private List<ASTModification> getModifications(IASTNode node, ModificationKind kind) { + Map<ModificationKind, List<ASTModification>> allModifications = getModifications(node); + List<ASTModification> modifications = allModifications.get(kind); + if (modifications == null) + return Collections.emptyList(); + return modifications; } - class CodeComparer { - private final StringBuilder originalCode; - private final StringBuilder synthCode; - private int lastCommonInSynthStart; - private int lastCommonInOriginalStart; - private int firstCommonInSynthEnd; - private int firstCommonInOriginalEnd; - - public CodeComparer(String originalCode, String synthCode) { - this.originalCode = new StringBuilder(originalCode); - this.synthCode = new StringBuilder(synthCode); - calculatePositions(); - } - - private void calculatePositions() { - lastCommonInSynthStart = calcLastCommonPositionInSynthCode(); - lastCommonInOriginalStart = calcLastCommonPositionInOriginalCode(); - firstCommonInSynthEnd = - calcFirstPositionOfCommonEndInSynthCode(lastCommonInSynthStart, lastCommonInOriginalStart); - firstCommonInOriginalEnd = - calcFirstPositionOfCommonEndInOriginalCode(lastCommonInOriginalStart, lastCommonInSynthStart); - trimTrailingNewlines(); - } - - private void trimTrailingNewlines() { - int prevOrigEnd = firstCommonInOriginalEnd - 1; - while (prevOrigEnd > lastCommonInOriginalStart && prevOrigEnd > -1 && - isUninterresting(originalCode, prevOrigEnd)) { - firstCommonInOriginalEnd = prevOrigEnd; - prevOrigEnd--; - } - - while (firstCommonInOriginalEnd > 0 && firstCommonInOriginalEnd + 1 < originalCode.length() && - (originalCode.charAt(firstCommonInOriginalEnd) == ' ' || originalCode.charAt(firstCommonInOriginalEnd) == '\t')) { - firstCommonInOriginalEnd++; - } - - int prevSynthEnd = firstCommonInSynthEnd - 1; - while (prevSynthEnd > lastCommonInSynthStart && prevSynthEnd > -1 && - isUninterresting(synthCode, prevSynthEnd)) { - firstCommonInSynthEnd = prevSynthEnd; - prevSynthEnd--; - } - while (firstCommonInSynthEnd > 0 && firstCommonInSynthEnd + 1 < synthCode.length() && - (synthCode.charAt(firstCommonInSynthEnd) == ' ' || synthCode.charAt(firstCommonInSynthEnd) == '\t')) { - firstCommonInSynthEnd++; - } - } - - public int getLastCommonPositionInSynthCode() { - return lastCommonInSynthStart; - } - - public int getLastCommonPositionInOriginalCode() { - return lastCommonInOriginalStart; - } - - public int getFirstPositionOfCommonEndInOriginalCode() { - return firstCommonInOriginalEnd; - } - - public int getFirstPositionOfCommonEndInSynthCode() { - return firstCommonInSynthEnd; - } - - public int calcLastCommonPositionInSynthCode() { - return findLastCommonPosition(synthCode, originalCode); - } - - public int calcLastCommonPositionInOriginalCode() { - return findLastCommonPosition(originalCode, synthCode); - } - - private int calcFirstPositionOfCommonEndInOriginalCode(int originalLimit, int synthLimit) { - StringBuilder reverseOriginalCode = new StringBuilder(originalCode).reverse(); - StringBuilder reverseSynthCode = new StringBuilder(synthCode).reverse(); - int lastCommonPosition = findLastCommonPosition(reverseOriginalCode, reverseSynthCode, - reverseOriginalCode.length() - originalLimit - 1, - reverseSynthCode.length() - synthLimit - 1); - - if (lastCommonPosition < 0 || lastCommonPosition >= originalCode.length()) { - return -1; - } - - return originalCode.length() - lastCommonPosition - 1; - } - - private int calcFirstPositionOfCommonEndInSynthCode(int synthLimit, int originalLimit) { - StringBuilder reverseOriginalCode = new StringBuilder(originalCode).reverse(); - StringBuilder reverseSynthCode = new StringBuilder(synthCode).reverse(); - - int lastCommonPosition = findLastCommonPosition(reverseSynthCode, reverseOriginalCode, - reverseSynthCode.length() - synthLimit - 1, - reverseOriginalCode.length() - originalLimit - 1); - - if (lastCommonPosition < 0 || lastCommonPosition >= synthCode.length()) { - return -1; - } - - return synthCode.length() - lastCommonPosition - 1; - } - - private int findLastCommonPosition(StringBuilder first, StringBuilder second) { - return findLastCommonPosition(first, second, first.length(), second.length()); - } - - private int findLastCommonPosition(StringBuilder first, StringBuilder second, int firstLimit, - int secondLimit) { - int firstIndex = -1; - int secondIndex = -1; - int lastCommonIndex = -1; - - do { - lastCommonIndex = firstIndex; - firstIndex = nextInterrestingPosition(first, firstIndex); - secondIndex = nextInterrestingPosition(second, secondIndex); - } while (firstIndex > -1 && firstIndex <= firstLimit && secondIndex > -1 && - secondIndex <= secondLimit && first.charAt(firstIndex) == second.charAt(secondIndex)); - return lastCommonIndex; - } - - private int nextInterrestingPosition(StringBuilder code, int position) { - do { - position++; - if (position >= code.length()) { - return -1; - } - } while (isUninterresting(code, position)); - return position; - } - - private boolean isUninterresting(StringBuilder code, int position) { - switch (code.charAt(position)) { - case ' ': - case '\n': - case '\r': - case '\t': + private boolean requiresRewrite(IASTNode node) { + if (!getModifications(node, ModificationKind.REPLACE).isEmpty()) + return true; + for (ASTModification modification : getModifications(node, ModificationKind.APPEND_CHILD)) { + if (!isAppendable(modification)) return true; - - default: - return false; - } - } - - protected void createChange(MultiTextEdit edit, IASTNode changedNode) { - int changeOffset = getOffsetIncludingComments(changedNode); - createChange(edit, changeOffset); - } - - private void createChange(MultiTextEdit edit, int changeOffset) { - int i = (firstCommonInSynthEnd >= 0 ? - firstCommonInOriginalEnd : originalCode.length()) - lastCommonInOriginalStart; - if (i <= 0) { - String insertCode = synthCode.substring(lastCommonInSynthStart, - firstCommonInSynthEnd); - InsertEdit iEdit = new InsertEdit(changeOffset + lastCommonInOriginalStart, - insertCode); - edit.addChild(iEdit); - } else if ((firstCommonInSynthEnd >= 0 ? - firstCommonInSynthEnd : synthCode.length()) - lastCommonInSynthStart <= 0) { - int correction = 0; - if (lastCommonInSynthStart > firstCommonInSynthEnd) { - correction = lastCommonInSynthStart - firstCommonInSynthEnd; - } - DeleteEdit dEdit = new DeleteEdit(changeOffset + lastCommonInOriginalStart, - firstCommonInOriginalEnd - lastCommonInOriginalStart + correction); - edit.addChild(dEdit); - } else { - String replacementCode = getReplacementCode(lastCommonInSynthStart, - firstCommonInSynthEnd); - ReplaceEdit rEdit = new ReplaceEdit( - changeOffset + Math.max(lastCommonInOriginalStart, 0), - (firstCommonInOriginalEnd >= 0 ? - firstCommonInOriginalEnd : - originalCode.length()) - Math.max(lastCommonInOriginalStart, 0), - replacementCode); - edit.addChild(rEdit); - } } + return false; + } - private String getReplacementCode(int lastCommonPositionInSynth, int firstOfCommonEndInSynth) { - int replacementStart = Math.max(lastCommonPositionInSynth, 0); - int replacementEnd = firstOfCommonEndInSynth >= 0 ? - firstOfCommonEndInSynth : synthCode.length(); - if (replacementStart < replacementEnd) { - return synthCode.substring(replacementStart, replacementEnd); - } - return ""; //$NON-NLS-1$ - } + private boolean isAppendable(ASTModification modification) { + if (modification.getKind() != ModificationKind.APPEND_CHILD) + return false; + IASTNode node = modification.getNewNode(); + return node instanceof IASTDeclaration || node instanceof IASTStatement; } public Change getChange() { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGeneratorWriterVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGeneratorWriterVisitor.java index 72537365b5d..7fd8a078166 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGeneratorWriterVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGeneratorWriterVisitor.java @@ -23,6 +23,7 @@ import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; import org.eclipse.cdt.core.dom.ast.IASTProblem; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; @@ -43,7 +44,6 @@ import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap; * @author Emanuel Graf IFS */ public class ChangeGeneratorWriterVisitor extends ASTWriterVisitor { - private static final String DEFAULT_INDENTATION = ""; //$NON-NLS-1$ private final ASTModificationStore modificationStore; private final String fileScope; private ModificationScopeStack stack; @@ -56,30 +56,31 @@ public class ChangeGeneratorWriterVisitor extends ASTWriterVisitor { this.fileScope = fileScope; this.stack = new ModificationScopeStack(modificationStore); - shouldVisitExpressions = delegateVisitor.shouldVisitExpressions; - shouldVisitStatements = delegateVisitor.shouldVisitStatements; - shouldVisitNames = delegateVisitor.shouldVisitNames; + shouldVisitArrayModifiers= delegateVisitor.shouldVisitArrayModifiers; + shouldVisitBaseSpecifiers = delegateVisitor.shouldVisitBaseSpecifiers; shouldVisitDeclarations = delegateVisitor.shouldVisitDeclarators; - shouldVisitDeclSpecifiers = delegateVisitor.shouldVisitDeclSpecifiers; shouldVisitDeclarators = delegateVisitor.shouldVisitDeclarators; + shouldVisitDeclSpecifiers = delegateVisitor.shouldVisitDeclSpecifiers; + shouldVisitExpressions = delegateVisitor.shouldVisitExpressions; shouldVisitInitializers = delegateVisitor.shouldVisitInitializers; - shouldVisitBaseSpecifiers = delegateVisitor.shouldVisitBaseSpecifiers; + shouldVisitNames = delegateVisitor.shouldVisitNames; shouldVisitNamespaces = delegateVisitor.shouldVisitNamespaces; - shouldVisitTemplateParameters = delegateVisitor.shouldVisitTemplateParameters; shouldVisitParameterDeclarations = delegateVisitor.shouldVisitParameterDeclarations; - shouldVisitTranslationUnit = delegateVisitor.shouldVisitTranslationUnit; + shouldVisitPointerOperators= delegateVisitor.shouldVisitPointerOperators; shouldVisitProblems = delegateVisitor.shouldVisitProblems; + shouldVisitStatements = delegateVisitor.shouldVisitStatements; + shouldVisitTemplateParameters = delegateVisitor.shouldVisitTemplateParameters; + shouldVisitTranslationUnit = delegateVisitor.shouldVisitTranslationUnit; shouldVisitTypeIds = delegateVisitor.shouldVisitTypeIds; - shouldVisitArrayModifiers= delegateVisitor.shouldVisitArrayModifiers; } public ChangeGeneratorWriterVisitor(ASTModificationStore modStore, NodeCommentMap nodeMap) { - this(modStore, DEFAULT_INDENTATION, null, nodeMap); + this(modStore, null, nodeMap); } - public ChangeGeneratorWriterVisitor(ASTModificationStore modStore, String givenIndentation, - String fileScope, NodeCommentMap commentMap) { - super(givenIndentation, commentMap); + public ChangeGeneratorWriterVisitor(ASTModificationStore modStore, String fileScope, + NodeCommentMap commentMap) { + super(commentMap); this.modificationStore = modStore; this.fileScope = fileScope; this.shouldVisitTranslationUnit = true; @@ -210,6 +211,12 @@ public class ChangeGeneratorWriterVisitor extends ASTWriterVisitor { } @Override + public int leave(IASTPointerOperator pointerOperator) { + super.leave(pointerOperator); + return PROCESS_SKIP; + } + + @Override public int leave(IASTProblem problem) { super.leave(problem); return PROCESS_SKIP; @@ -306,6 +313,14 @@ public class ChangeGeneratorWriterVisitor extends ASTWriterVisitor { } @Override + public int visit(IASTPointerOperator pointerOperator) { + if (doBeforeEveryNode(pointerOperator) == PROCESS_CONTINUE) { + return super.visit(pointerOperator); + } + return PROCESS_SKIP; + } + + @Override public int visit(IASTProblem problem) { if (doBeforeEveryNode(problem) == PROCESS_CONTINUE) { return super.visit(problem); @@ -369,10 +384,8 @@ public class ChangeGeneratorWriterVisitor extends ASTWriterVisitor { stack.pushScope(node); currentMod.getNewNode().accept(this); stack.popScope(node); - return PROCESS_SKIP; - } else { - return PROCESS_SKIP; } + return PROCESS_SKIP; } } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java index 25620836f9d..d6defc940b4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java @@ -13,6 +13,7 @@ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.TreeMap; import org.eclipse.cdt.core.dom.ast.ASTVisitor; @@ -171,26 +172,25 @@ public class ASTCommenter { * @return NodeCommentMap */ public static NodeCommentMap getCommentedNodeMap(IASTTranslationUnit transUnit){ - if (transUnit== null) { + if (transUnit == null) { return new NodeCommentMap(); } - ArrayList<IASTComment> comments = removeNotNeededComments(transUnit); - if (comments == null || comments.size() == 0) { + List<IASTComment> comments = removeNotNeededComments(transUnit); + if (comments == null || comments.isEmpty()) { return new NodeCommentMap(); } return addCommentsToCommentMap(transUnit, comments); } - private static ArrayList<IASTComment> removeNotNeededComments(IASTTranslationUnit transUnit) { - ArrayList<IASTComment> comments = getCommentsInWorkspace(transUnit); - if (comments == null || comments.size() == 0) { + private static List<IASTComment> removeNotNeededComments(IASTTranslationUnit transUnit) { + List<IASTComment> comments = getCommentsInWorkspace(transUnit); + if (comments == null || comments.isEmpty()) { return null; } - ArrayList<IASTComment> com = removeAllPreprocessorComments(transUnit, comments); - return com; + return removeAllPreprocessorComments(transUnit, comments); } - private static ArrayList<IASTComment> getCommentsInWorkspace(IASTTranslationUnit tu) { + private static List<IASTComment> getCommentsInWorkspace(IASTTranslationUnit tu) { IASTComment[] comments = tu.getComments(); ArrayList<IASTComment> commentsInWorkspace = new ArrayList<IASTComment>(); @@ -206,8 +206,8 @@ public class ASTCommenter { return commentsInWorkspace; } - private static ArrayList<IASTComment> removeAllPreprocessorComments(IASTTranslationUnit tu, - ArrayList<IASTComment> comments) { + private static List<IASTComment> removeAllPreprocessorComments(IASTTranslationUnit tu, + List<IASTComment> comments) { IASTPreprocessorStatement[] preprocessorStatements = tu.getAllPreprocessorStatements(); TreeMap<Integer, String> treeOfPreProcessorLines = new TreeMap<Integer,String>(); TreeMap<String, ArrayList<Integer>> ppOffsetForFiles = new TreeMap<String, ArrayList<Integer>>(); @@ -286,7 +286,7 @@ public class ASTCommenter { } private static NodeCommentMap addCommentsToCommentMap(IASTTranslationUnit rootNode, - ArrayList<IASTComment> comments){ + List<IASTComment> comments){ NodeCommentMap commentMap = new NodeCommentMap(); CommentHandler commHandler = new CommentHandler(comments); @@ -296,8 +296,8 @@ public class ASTCommenter { ASTCommenterVisitor commenter = new ASTCommenterVisitor(commHandler, commentMap); declarations[i].accept(commenter); - //add remaining comments to the last declaration => Comments won't get lost - if (i + 1 == declarations.length) { + // Add the remaining comments to the last declaration to prevent comment loss. + if (i == declarations.length - 1) { commenter.addRemainingComments(declarations[i]); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java index 6d7b1e26776..9fbd6bbf19f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java @@ -45,17 +45,18 @@ public class ASTCommenterVisitor extends ASTVisitor { private NodeCommenter nodeCommenter; { - shouldVisitExpressions = true; - shouldVisitStatements = true; - shouldVisitNames = true; + shouldVisitBaseSpecifiers = true; shouldVisitDeclarations = true; - shouldVisitDeclSpecifiers = true; shouldVisitDeclarators = true; + shouldVisitDeclSpecifiers = true; + shouldVisitExpressions = true; shouldVisitInitializers = true; - shouldVisitBaseSpecifiers = true; + shouldVisitNames = true; shouldVisitNamespaces = true; - shouldVisitTemplateParameters = true; shouldVisitParameterDeclarations = true; + shouldVisitStatements = true; + shouldVisitTemplateParameters = true; + shouldVisitTypeIds = true; } public ASTCommenterVisitor(CommentHandler commHandler, NodeCommentMap commentMap) { @@ -93,6 +94,11 @@ public class ASTCommenterVisitor extends ASTVisitor { } @Override + public int visit(IASTTypeId typeId) { + return nodeCommenter.appendComments((ASTNode) typeId); + } + + @Override public int visit(IASTDeclaration declaration) { return nodeCommenter.appendComments((ASTNode) declaration); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/CommentHandler.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/CommentHandler.java index 9299f42963b..9ef98352418 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/CommentHandler.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/CommentHandler.java @@ -7,11 +7,11 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Institute for Software - initial API and implementation + * Institute for Software - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; -import java.util.ArrayList; +import java.util.List; import org.eclipse.cdt.core.dom.ast.IASTComment; @@ -23,10 +23,9 @@ import org.eclipse.cdt.core.dom.ast.IASTComment; * @author Guido Zgraggen IFS */ public class CommentHandler { - - private final ArrayList<IASTComment> comments; + private final List<IASTComment> comments; - public CommentHandler(ArrayList<IASTComment> comments) { + public CommentHandler(List<IASTComment> comments) { super(); this.comments = comments; } @@ -36,7 +35,7 @@ public class CommentHandler { } public boolean hasMore() { - return comments.size()>0; + return !comments.isEmpty(); } public IASTComment getFirst() { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java index 4f65d336710..829c2ddd4e2 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommentMap.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Copyright (c) 2008, 2011 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -7,12 +7,14 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Institute for Software - initial API and implementation + * Institute for Software - initial API and implementation ******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTNode; @@ -25,18 +27,18 @@ import org.eclipse.cdt.core.dom.ast.IASTNode; * @author Guido Zgraggen IFS */ public class NodeCommentMap { - protected final HashMap<IASTNode, ArrayList<IASTComment>> leadingMap = new HashMap<IASTNode, ArrayList<IASTComment>>(); - protected final HashMap<IASTNode, ArrayList<IASTComment>> trailingMap = new HashMap<IASTNode, ArrayList<IASTComment>>(); - protected final HashMap<IASTNode, ArrayList<IASTComment>> freestandingMap = new HashMap<IASTNode, ArrayList<IASTComment>>(); + protected final Map<IASTNode, List<IASTComment>> leadingMap = new HashMap<IASTNode, List<IASTComment>>(); + protected final Map<IASTNode, List<IASTComment>> trailingMap = new HashMap<IASTNode, List<IASTComment>>(); + protected final Map<IASTNode, List<IASTComment>> freestandingMap = new HashMap<IASTNode, List<IASTComment>>(); /** * Add a comment to the map with the trailing comments. * @param node The node is the key. * @param comment The comment is the value */ - public void addTrailingCommentToNode(IASTNode node, IASTComment comment){ - ArrayList<IASTComment> comments = trailingMap.get(node); - if(comments == null){ + public void addTrailingCommentToNode(IASTNode node, IASTComment comment) { + List<IASTComment> comments = trailingMap.get(node); + if (comments == null) { comments = new ArrayList<IASTComment>(); } comments.add(comment); @@ -44,17 +46,15 @@ public class NodeCommentMap { } /** - * Returns an ArrayList for the given node. This ArrayList contains all the comments + * Returns a List for the given node. This List contains all the comments * which are assigned to this specific node. If no comments are available an empty - * ArrayList is returned. + * List is returned. * @param node The key to fetch the associated comments. - * @return ArrayList + * @return List */ - public ArrayList<IASTComment> getTrailingCommentsForNode(IASTNode node){ - if(trailingMap.get(node) == null) { - return new ArrayList<IASTComment>(); - } - return trailingMap.get(node); + public List<IASTComment> getTrailingCommentsForNode(IASTNode node) { + List<IASTComment> list = trailingMap.get(node); + return list != null ? list : new ArrayList<IASTComment>(); } /** @@ -62,9 +62,9 @@ public class NodeCommentMap { * @param node The node is the key. * @param comment The comment is the value */ - public void addLeadingCommentToNode(IASTNode node, IASTComment comment){ - ArrayList<IASTComment> comments = leadingMap.get(node); - if(comments == null){ + public void addLeadingCommentToNode(IASTNode node, IASTComment comment) { + List<IASTComment> comments = leadingMap.get(node); + if (comments == null) { comments = new ArrayList<IASTComment>(); } comments.add(comment); @@ -72,17 +72,15 @@ public class NodeCommentMap { } /** - * Returns an ArrayList for the given node. This ArrayList contains all the comments + * Returns a List for the given node. This List contains all the comments * which are assigned to this specific node. If no comments are available an empty - * ArrayList is returned. + * List is returned. * @param node The key to fetch the associated comments. - * @return ArrayList + * @return List */ - public ArrayList<IASTComment> getLeadingCommentsForNode(IASTNode node){ - if(leadingMap.get(node) == null) { - return new ArrayList<IASTComment>(); - } - return leadingMap.get(node); + public List<IASTComment> getLeadingCommentsForNode(IASTNode node) { + List<IASTComment> list = leadingMap.get(node); + return list != null ? list : new ArrayList<IASTComment>(); } /** @@ -90,9 +88,9 @@ public class NodeCommentMap { * @param node The node is the key. * @param comment The comment is the value */ - public void addFreestandingCommentToNode(IASTNode node, IASTComment comment){ - ArrayList<IASTComment> comments = freestandingMap.get(node); - if(comments == null){ + public void addFreestandingCommentToNode(IASTNode node, IASTComment comment) { + List<IASTComment> comments = freestandingMap.get(node); + if (comments == null) { comments = new ArrayList<IASTComment>(); } comments.add(comment); @@ -100,51 +98,50 @@ public class NodeCommentMap { } /** - * Returns an ArrayList for the given node. This ArrayList contains all the comments + * Returns a List for the given node. This List contains all the comments * which are assigned to this specific node. If no comments are available an empty - * ArrayList is returned. + * List is returned. * @param node The key to fetch the associated comments. - * @return ArrayList + * @return List */ - public ArrayList<IASTComment> getFreestandingCommentsForNode(IASTNode node){ - if(freestandingMap.get(node) == null) { - return new ArrayList<IASTComment>(); - } - return freestandingMap.get(node); + public List<IASTComment> getFreestandingCommentsForNode(IASTNode node) { + List<IASTComment> list = freestandingMap.get(node); + return list != null ? list : new ArrayList<IASTComment>(); } - - + /** - * Returns the HashMap with all leading maps. Used only for test purpose - * @return HashMap of all leading comments + * Returns the Map with all leading maps. Used only for test purpose + * @return Map of all leading comments */ - public HashMap<IASTNode, ArrayList<IASTComment>> getLeadingMap() { + public Map<IASTNode, List<IASTComment>> getLeadingMap() { return leadingMap; } + /** - * Returns the HashMap with all trailing maps. Used only for test purpose - * @return HashMap of all trailing comments + * Returns the Map with all trailing maps. Used only for test purpose + * @return Map of all trailing comments */ - public HashMap<IASTNode, ArrayList<IASTComment>> getTrailingMap() { + public Map<IASTNode, List<IASTComment>> getTrailingMap() { return trailingMap; } + /** - * Returns the HashMap with all freestanding maps. Used only for test purpose - * @return HashMap of all freestanding comments + * Returns the Map with all freestanding maps. Used only for test purpose + * @return Map of all freestanding comments */ - public HashMap<IASTNode, ArrayList<IASTComment>> getFreestandingMap() { + public Map<IASTNode, List<IASTComment>> getFreestandingMap() { return freestandingMap; } /** - * Returns an ArrayList for the given node. This ArrayList contains all the comments + * Returns an List for the given node. This List contains all the comments * which are assigned to this specific node. If no comments are available an empty - * ArrayList is returned. + * List is returned. * @param node The key to fetch the associated comments. - * @return ArrayList + * @return List */ - public ArrayList<IASTComment> getAllCommentsForNode(IASTNode node) { - ArrayList<IASTComment> comment = new ArrayList<IASTComment>(); + public List<IASTComment> getAllCommentsForNode(IASTNode node) { + List<IASTComment> comment = new ArrayList<IASTComment>(); comment.addAll(getFreestandingCommentsForNode(node)); comment.addAll(getLeadingCommentsForNode(node)); comment.addAll(getTrailingCommentsForNode(node)); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java index 5ae49e8f7dc..75430528969 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java @@ -7,7 +7,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Institute for Software - initial API and implementation + * Institute for Software - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; @@ -39,25 +39,27 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** - * The NodeCommenter contains all the logic that is needed for the ASTCommentVisitor to assign the comments - * to the suitable node. Together with the ASTCommenterVisitor it fills all the comments with the correspondent - * node into the NodeCommentMap. + * The NodeCommenter contains all the logic that is needed for the ASTCommentVisitor to assign + * the comments to the suitable node. Together with the ASTCommenterVisitor it fills all + * the comments with the correspondent node into the NodeCommentMap. * - * Following, a little explanation of the assignment logic. It is only a loose illustration a detailed description - * would include a combined explanation of ASTCommenterVisitor and NodeCommenter.<br> + * Following, a little explanation of the assignment logic. It is only a loose illustration + * a detailed description would include a combined explanation of ASTCommenterVisitor and + * NodeCommenter.<br> * To understand the logic we define the three types of comments:<br> * leading comments - Comments before a statement, declaration, or definition.<br> * trailing comments - Comments right after the AST node on the same line.<br> * freestanding comments - Comments before a closing brace such as they occur in * namespace-, class- and method-definitions or at the end of a file.<br> * - * The first comment is fetched and the position of it is compared to the position of the actual node. If - * the position of the comment is smaller than the comment is added to the node as leading. If it is behind the node - * but on the same line it is added as trailing. If one of these possibilities match the next comment is fetched for - * the same check. If it doesn't match the same procedure is done for all the child nodes. After checking the sub nodes - * the actual node is checked again if the comment is trailing. Then there is also the possibility that this comment is - * freestanding. This is the case when the comment is not added to any child node but the position is smaller den - * the end position of the node. + * The first comment is fetched and the position of it is compared to the position of the actual + * node. If the position of the comment is smaller than the comment is added to the node as leading. + * If it is behind the node but on the same line it is added as trailing. If one of these + * possibilities match the next comment is fetched for the same check. If it doesn't match the same + * procedure is done for all the child nodes. After checking the sub nodes the actual node is + * checked again if the comment is trailing. Then there is also the possibility that this comment is + * freestanding. This is the case when the comment is not added to any child node but the position + * is smaller than the end position of the node. * * @author Guido Zgraggen IFS */ @@ -90,7 +92,7 @@ public class NodeCommenter { ASTNode com = (ASTNode) comment; if (node.getFileLocation() == null) { - //MacroExpansions have no FileLocation + // MacroExpansions have no FileLocation return false; } @@ -111,7 +113,7 @@ public class NodeCommenter { ASTNode com = (ASTNode) comment; if (node.getFileLocation() == null) { - //MacroExpansions have no Filelocation + // MacroExpansions have no FileLocation return false; } if (OffsetHelper.getNodeEndPoint(com) <= OffsetHelper.getNodeEndPoint(node)) { @@ -151,7 +153,7 @@ public class NodeCommenter { try { InputStream is = file.getContents(); - int length = OffsetHelper.getNodeOffset(com)-OffsetHelper.getNodeEndPoint(node); + int length = OffsetHelper.getNodeOffset(com) - OffsetHelper.getNodeEndPoint(node); byte[] b = new byte[length]; long count = is.skip(OffsetHelper.getEndOffsetWithoutComments(node)); |