Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: ec0d9f9afc8e4a1c2a2ae80c64da54a1e66ddc81 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*******************************************************************************
 * Copyright (c) 2008, 2010 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 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html  
 *  
 * Contributors: 
 *     Institute for Software - initial API and implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarationStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTForStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDefinition;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIfStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLabelStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLinkageSpecification;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSwitchStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTWhileStatement;

/**
 * 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>
 * 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 than the end position of the node. 
 * 
 * @author Guido Zgraggen IFS
 */
public class NodeCommenter {
	protected ASTVisitor visitor;
	protected CommentHandler commHandler;
	protected NodeCommentMap commentMap;
	protected List<IASTNode> children;

	public NodeCommenter(ASTVisitor visitor, CommentHandler commHandler, NodeCommentMap commentMap) {
		this.visitor = visitor;
		this.commHandler = commHandler;
		this.commentMap = commentMap;
		this.children = new ArrayList<IASTNode>();
	}

	protected void writeNodeList(IASTNode[] nodes) {
		for (int i = 0; i < nodes.length; ++i) {
			nodes[i].accept(visitor);
		}
	}
	
	protected void visitNodeIfNotNull(IASTNode node){
		if (node != null){
			node.accept(visitor);
		}
	}

	protected boolean appendComment(ASTNode node, IASTComment comment) {
		ASTNode com = (ASTNode) comment;

		if (node.getFileLocation() == null) {
			// MacroExpansions have no FileLocation
			return false;
		}
				
		int nodeLineNumber = getEndingLineNumber(node);
		int commentLineNumber= getStartingLineNumber(comment);
	
		if (getNodeEndPoint(com) <= getNodeOffset(node)) {
			addLeadingCommentToMap(node, comment);
			return true;
		} else if (isTrailing(node, com, nodeLineNumber, commentLineNumber)) {
			addTrailingCommentToMap(node, comment);
			return true;
		}
		return false;
	}
	
	protected boolean appendFreestandingComment(ASTNode node, IASTComment comment) {
		ASTNode com = (ASTNode) comment;

		if (node.getFileLocation() == null) {
			// MacroExpansions have no FileLocation
			return false;
		}
		if (getNodeEndPoint(com) <= getNodeEndPoint(node)) {
			addFreestandingCommentToMap(node, comment);
			return true;
		}
		return false;
	}

	private void addLeadingCommentToMap(ASTNode node, IASTComment comment) {
		commentMap.addLeadingCommentToNode(node, comment);
		commHandler.allreadyAdded(comment);
	}

	private void addTrailingCommentToMap(ASTNode node, IASTComment comment) {
		commentMap.addTrailingCommentToNode(node, comment);
		commHandler.allreadyAdded(comment);
	}

	private void addFreestandingCommentToMap(ASTNode node, IASTComment comment) {
		commentMap.addFreestandingCommentToNode(node, comment);
		commHandler.allreadyAdded(comment);
	}

	private boolean isTrailing(ASTNode node, ASTNode com, int nodeLineNumber, int commentLineNumber) {
		if (nodeLineNumber != commentLineNumber || 
				getNodeOffset(com) < getNodeEndPoint(node) || 
				!canNotBeAddedToParent(node, com) ||
				mustBeAddedToSubnodes(node)) {
			return false;
		}
		if (getNodeOffset(com) < getNodeEndPoint(node) + 2) {
			return true;
		}
		String code = node.getTranslationUnit().getRawSignature();
		int commentOffset = getNodeOffset(com) - getNodeEndPoint(node) + getNodeEndOffset(node);
		for (int offset = getNodeEndOffset(node); offset < commentOffset; offset++) {
			if (!Character.isWhitespace(code.charAt(offset)))
				return false;
		}
		return true;
	}
	private boolean canNotBeAddedToParent(ASTNode node, ASTNode com) {
		ASTNode parent = (ASTNode) node.getParent();
		
		if (hasNodeSameEndingAsSubnode(parent)) {
			return true;
		} else if (parent instanceof IASTTranslationUnit) {
			return true;
		} else if (parent instanceof ICPPASTTemplateDeclaration) {
			return true;
		} else if (parent instanceof CPPASTIfStatement) {
			return true;
		} else if (parent instanceof ICPPASTBaseSpecifier) {
			parent = (ASTNode) parent.getParent();
		}
		return getNodeOffset(com) < getNodeEndPoint(parent);
	}

	private boolean mustBeAddedToSubnodes(ASTNode node) {
		return hasNodeSameEndingAsSubnode(node);
	}
	
	private boolean hasNodeSameEndingAsSubnode(ASTNode node) {
		if (node instanceof CPPASTFunctionDefinition) {
			return true;
		} else if (node instanceof CPPASTDeclarationStatement) {
			return true;
		} else if (node instanceof CPPASTForStatement) {
			return true;
		} else if (node instanceof CPPASTLabelStatement) {
			return true;
		} else if (node instanceof CPPASTIfStatement) {
			return true;
		} else if (node instanceof CPPASTSwitchStatement) {
			return true;
		} else if (node instanceof CPPASTWhileStatement) {
			return true;
		} else if (node instanceof CPPASTTemplateDeclaration) {
			return true;
		} else if (node instanceof CPPASTLinkageSpecification) {
			return true;
		} else if (node instanceof CPPASTExplicitTemplateInstantiation) {
			return true;
		}
		return false;
	}
	
	protected int appendComments(ASTNode node) {
		while (commHandler.hasMore()) {
			IASTComment comment = commHandler.getFirst();
			
			if (isNotSameFile(node, comment)) {
				return ASTVisitor.PROCESS_SKIP;
			}
			
			if (!appendComment(node, comment)) {
				return ASTVisitor.PROCESS_CONTINUE;
			}
		}
		return ASTVisitor.PROCESS_ABORT;
	}
	
	protected int appendFreestandingComments(ASTNode node) {
		while (commHandler.hasMore()) {
			IASTComment comment = commHandler.getFirst();
			
			if (isNotSameFile(node, comment)) {
				return ASTVisitor.PROCESS_SKIP;
			}
			
			if (appendComment(node, comment)) {
				return ASTVisitor.PROCESS_CONTINUE;
			}
			
			if (!appendFreestandingComment(node, comment)) {
				return ASTVisitor.PROCESS_CONTINUE;
			}
		}
		return ASTVisitor.PROCESS_ABORT;
	}
	
	public void appendRemainingComments(IASTDeclaration declaration) {
		while (commHandler.hasMore()) {
			IASTComment comment = commHandler.getFirst();
			if (appendComment((ASTNode)declaration, comment)) {
				continue;
			}
			addFreestandingCommentToMap((ASTNode) declaration, comment);
		}
	}
	
	private boolean isNotSameFile(IASTNode node, IASTComment comment) {
		if (node.getFileLocation() == null) {
			return true;
		}
		return !node.getFileLocation().getFileName().equals(comment.getFileLocation().getFileName());
	}

	private static int getNodeEndOffset(IASTNode node) {
		IASTFileLocation fileLocation = node.getFileLocation();
		return fileLocation.getNodeOffset() + fileLocation.getNodeLength();
	}
	
	private static int getNodeOffset(ASTNode node) {
		return node.getOffset();
	}

	private static int getNodeEndPoint(ASTNode node) {
		return node.getOffset() + node.getLength();
	}

	private static int getStartingLineNumber(IASTNode node) {
		return node.getFileLocation().getStartingLineNumber();
	}

	private static int getEndingLineNumber(IASTNode node) {
		return node.getFileLocation().getEndingLineNumber();
	}
}

Back to the top