• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moosetechnology / VerveineJ / 20993219095

14 Jan 2026 11:56AM UTC coverage: 51.4% (+0.2%) from 51.203%
20993219095

Pull #190

github

web-flow
Merge 06681983b into c134c85f9
Pull Request #190: fix #186, rolling back some of the changes in #184

1931 of 3948 branches covered (48.91%)

Branch coverage included in aggregate %.

13 of 14 new or added lines in 4 files covered. (92.86%)

2 existing lines in 2 files now uncovered.

4294 of 8163 relevant lines covered (52.6%)

2.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.04
/app/src/main/java/fr/inria/verveine/extractor/java/visitors/defvisitors/VisitorComments.java
1
package fr.inria.verveine.extractor.java.visitors.defvisitors;
2

3
import java.io.BufferedReader;
4
import java.io.FileInputStream;
5
import java.io.FileNotFoundException;
6
import java.io.IOException;
7
import java.io.InputStream;
8
import java.io.InputStreamReader;
9
import java.io.UnsupportedEncodingException;
10
import java.util.List;
11

12
import org.eclipse.jdt.core.dom.ASTNode;
13
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
14
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
15
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
16
import org.eclipse.jdt.core.dom.Comment;
17
import org.eclipse.jdt.core.dom.CompilationUnit;
18
import org.eclipse.jdt.core.dom.EnumDeclaration;
19
import org.eclipse.jdt.core.dom.Initializer;
20
import org.eclipse.jdt.core.dom.Javadoc;
21
import org.eclipse.jdt.core.dom.MethodDeclaration;
22
import org.eclipse.jdt.core.dom.TypeDeclaration;
23
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
24
import org.moosetechnology.model.famix.famixjavaentities.AnnotationTypeAttribute;
25
import org.moosetechnology.model.famix.famixjavaentities.Method;
26
import org.moosetechnology.model.famix.famixtraits.TCanBeStub;
27
import org.moosetechnology.model.famix.famixtraits.TWithAttributes;
28
import org.moosetechnology.model.famix.famixtraits.TWithComments;
29

30
import fr.inria.verveine.extractor.java.EntityDictionary;
31
import fr.inria.verveine.extractor.java.VerveineJOptions;
32
import fr.inria.verveine.extractor.java.visitors.GetVisitedEntityAbstractVisitor;
33
/**
34
 * A class to collect all comments.
35
 * Some important details on comments in JDT:
36
 * <ul>
37
 * <li>only JavaDoc comment are associated to any entity node in the AST (the entity immediately after the javadoc)</li>
38
 * <li>The <code>startPosition</code> of a node with a JavaDoc is actually the <code>startPosition</code> of the JavaDoc
39
 *   (ie. the JavaDoc is considered to be part of the entity declaration)</li> 
40
 * <li>line and block comment are recorded in a list, but we have to decide to what entity they will be associated.
41
 *   We put them in the method containing them or in the method or attribute immediately after them if they are outside a method</li>
42
 * <li>content (text) of block or line comments is not stored, only their position and length.
43
 *   We have to recover them from the source file</li> 
44
 * </ul>
45
 * 
46
 * This class assumes the AST is visited in the file position order of the nodes (nodes appearing first in the file
47
 * are visited first).
48
 * It visit the AST in parallel with "visiting" the list of comments and for each comment,
49
 * <ul>
50
 * <li>if it is a JavaDoc, gets its associated node</li>
51
 * <li>if not javadoc, finds out if it is in a method or not
52
 *         <ul>
53
 *  <li>if not, it find the class, method, or attribute that comes immediately after the comment</li>
54
 *  <li>if in a method, it associates the comment to this method</li>
55
 *  </ul>
56
 * </ul>
57
 * 
58
 * @author anquetil
59
 */
60
public class VisitorComments extends GetVisitedEntityAbstractVisitor {
61

62
        /**
63
         * list of all comments in the compilation unit
64
         */
65
        protected List<Comment> allComments;
66

67
        /**
68
         * Indice in {@link #allComments} of next comment to deal with (ie. associate with a Famix entity).
69
         */
70
        protected int nextComment;
71

72
        /**
73
         * Whether we are defining a class member or not<br>
74
         * Used to differentiate attribute declarations from other variable declarations 
75
         */
76
        protected boolean classMemberDeclarations;
77

78
        /**
79
         * To handle correctly AnonymousClassDeclaration, we need to keep the start position of methods
80
         * (that might contain these anonymous classes)
81
         */
82
        private int methodStartPosition;
83

84
        protected String currentFilename = null;
3✔
85
        protected BufferedReader openedFile = null;
3✔
86
        protected int lastPositionRead = 0;
3✔
87

88

89
        public VisitorComments(EntityDictionary dico, VerveineJOptions options) {
90
                super(dico, options);
4✔
91
                classMemberDeclarations = false;
3✔
92
        }
1✔
93

94
        // VISITOR METHODS
95

96
        @Override
97
        public boolean visit(CompilationUnit node) {
98
                initializeCommentsReader(node);
3✔
99
                if (allComments.size() == 0) {
4✔
100
                        // no comment, not visiting
101
                        return false;
2✔
102
                }
103
                else {
104
                        nextComment = 0;
3✔
105
                }
106
                return super.visit(node);
4✔
107
        }
108

109
        @Override
110
        public void endVisit(CompilationUnit node) {
111
                endVisitCompilationUnit(node);
3✔
112
                closeFile();
2✔
113
        }
1✔
114

115
        @Override
116
        public boolean visit(TypeDeclaration node) {
117
                TWithComments fmx = (TWithComments) visitTypeDeclaration(node);
5✔
118

119
                assignCommentsBefore(node, node.getJavadoc(), fmx);
6✔
120
                classMemberDeclarations = true;
3✔
121

122
                return super.visit(node);
4✔
123
        }
124

125
        @Override
126
        public void endVisit(TypeDeclaration node) {
127
                classMemberDeclarations = false;
3✔
128
                endVisitTypeDeclaration(node);
3✔
129
        }
1✔
130

131
        /**
132
         * AnonymousClassDeclaration appear within a method, we deal first with all "pending" comments
133
         * that appear before the AnonymousClassDeclaration and should be associated with the method and not anonymous class
134
         */
135
        @Override
136
        public boolean visit(AnonymousClassDeclaration node) {
137
                assignCommentsInInterval( methodStartPosition, node.getStartPosition(), (TWithComments) context.topMethod());
10✔
138
                classMemberDeclarations = true;
3✔
139

140
                return super.visit(node);
4✔
141
        }
142

143
        @Override
144
        public void endVisit(AnonymousClassDeclaration node) {
145
                classMemberDeclarations = false;
3✔
146
                endVisitAnonymousClassDeclaration( node);
3✔
147
        }
1✔
148

149
        @Override
150
        public boolean visit(EnumDeclaration node) {
151
                TWithComments fmx = visitEnumDeclaration( node);
4✔
152

153
                assignCommentsBefore(node, node.getJavadoc(), fmx);
6✔
154
                classMemberDeclarations = true;
3✔
155

156
                return super.visit(node);
4✔
157
        }
158

159
        @Override
160
        public void endVisit(EnumDeclaration node) {
161
                classMemberDeclarations = false;
3✔
162
                endVisitEnumDeclaration( node);
3✔
163
        }
1✔
164

165
        @Override
166
        public boolean visit(AnnotationTypeDeclaration node) {
167
                TWithComments fmx = visitAnnotationTypeDeclaration( node);
4✔
168

169
                assignCommentsBefore(node, node.getJavadoc(), fmx);
6✔
170
                classMemberDeclarations = true;
3✔
171

172
                return super.visit(node);
4✔
173
        }
174

175
        @Override
176
        public void endVisit(AnnotationTypeDeclaration node) {
177
                classMemberDeclarations = false;
3✔
178
                endVisitAnnotationTypeDeclaration(node);
3✔
179
        }
1✔
180

181
        @Override
182
        public boolean visit(MethodDeclaration node) {
183
                Method fmx = visitMethodDeclaration( node);
4✔
184

185
                assignCommentsBefore(node, node.getJavadoc(), fmx);
6✔
186
                classMemberDeclarations = false;
3✔
187
                methodStartPosition = node.getStartPosition();
4✔
188

189
                return super.visit(node);
4✔
190
        }
191

192
        @Override
193
        public void endVisit(MethodDeclaration node) {
194
                assignCommentsInside(node, (TWithComments) context.topMethod());
7✔
195
                classMemberDeclarations = true;
3✔
196
                endVisitMethodDeclaration(node);
3✔
197
        }
1✔
198

199
        public boolean visit(AnnotationTypeMemberDeclaration node) {
200
                AnnotationTypeAttribute fmx = visitAnnotationTypeMemberDeclaration( node);
4✔
201

202
                assignCommentsBefore(node, node.getJavadoc(), fmx);
6✔
203

204
                return super.visit(node);
4✔
205
        }
206

207
        @Override
208
        public boolean visit(Initializer node) {
209
                visitInitializer(node);
4✔
210
                classMemberDeclarations = false;
3✔
211
                return super.visit(node);
4✔
212
        }
213

214
        @Override
215
        public void endVisit(Initializer node) {
216
                classMemberDeclarations = true;
3✔
217
                endVisitInitializer(node);
3✔
218
        }
1✔
219

220
        @Override
221
        public boolean visit(VariableDeclarationFragment node) {
222
                if (classMemberDeclarations) {
3✔
223
                        TWithComments fmx = dico.getFamixAttribute(node.resolveBinding(), node.getName().getIdentifier(), (TWithAttributes) context.topType());
13✔
224
                        if ( ! ((TCanBeStub) fmx).getIsStub() ) {
5!
225
                                // if it is a stub, it might have been created by the getFamixAttribute just above
226
                                // Anyway we cannot have a comment on a stub
227
                                assignCommentsBefore(node, /*optionalJavadoc*/null, fmx);
5✔
228
                        }
229
                }
230

231
                return super.visit(node);
4✔
232
        }
233

234
        // UTILITY METHODS
235

236
        /**
237
         * Assigns all "pending" comments to the Famix entity.<br>
238
         * Pending comments are those not treated that appear before the <code>node</code> associated with the Famix entity.<br>
239
         * <code>optionalJavadoc</code> is an optional Javadoc comment (!) that could be associated to the <code>node</code>.
240
         * If this is the case, the real <code>startPosition</code> of the <code>node</code> must be incremented of the length of the javadoc
241
         * because this one is considered part of the <code>node</code>.
242
         */
243
        protected void assignCommentsBefore(ASTNode node, Javadoc optionalJavadoc, TWithComments fmx) {
244
                int start = node.getStartPosition();
3✔
245
                if (optionalJavadoc != null) {
2✔
246
                        start += optionalJavadoc.getLength();
5✔
247
                }
248
                assignCommentsInInterval( 0, start, fmx);
5✔
249
        }
1✔
250

251
        /**
252
         * Assigns all "pending" comments that appear with the Famix entity (actually within the <code>node</code> associated to it) 
253
         */
254
        protected void assignCommentsInside(ASTNode node, TWithComments fmx) {
255
                assignCommentsInInterval( node.getStartPosition(), node.getStartPosition() + node.getLength(), fmx);
10✔
256
        }
1✔
257
        
258
        /**
259
         * Assigns to the Famix entity all "pending" comments that appear within the interval [start ; stop] 
260
         */
261
        protected void assignCommentsInInterval(int start, int end, TWithComments fmx) {
262
                boolean searchComment = true;
2✔
263

264
                if (fmx == null) {
2!
UNCOV
265
                        return;
×
266
                }
267

268
                while ( searchComment && pendingComments() ) {
5✔
269
                        Comment cmt = allComments.get(nextComment);
7✔
270
                        if (commentIsInside(cmt, start, end)) {
6✔
271
                                commentCreation( cmt, fmx);
4✔
272
                                nextComment++;
7✔
273
                        }
274
                        else {
275
                                searchComment = false;
2✔
276
                        }
277
                }                
1✔
278
        }
1✔
279

280
        private void commentCreation(Comment cmt, TWithComments fmx) {
281
                if (options.commentsAsText()) {
4✔
282
                        
283
                        dico.createFamixComment(cmt, fmx, getFileContent(cmt.getStartPosition(), cmt.getLength()));
13✔
284
                }
285
                else {
286
                        dico.createFamixComment(cmt, fmx);
6✔
287
                }
288
        }
1✔
289

290
        /**
291
         * Whether there is still some "pending" comments
292
         */
293
        protected boolean pendingComments() {
294
                return nextComment < allComments.size();
10✔
295
        }
296

297
        /**
298
         * whether <code>cmt</code> appears inside interval [ start ; end ] 
299
         */
300
        protected boolean commentIsInside(Comment cmt, int start, int end) {
301
                return (start <= cmt.getStartPosition()) && (end >= cmt.getStartPosition() + cmt.getLength());
15!
302
        }
303

304
        protected void initializeCommentsReader(CompilationUnit node) {
305
                allComments = node.getCommentList();
4✔
306

307
                currentFilename = (String) ((CompilationUnit)node).getProperty(EntityDictionary.SOURCE_FILENAME_PROPERTY);
6✔
308
                try {
309
                        InputStream is = new FileInputStream(currentFilename);
6✔
310
                        this.openedFile = new BufferedReader(new InputStreamReader(is, options.getFileEncoding()));
12✔
311

312
                        this.lastPositionRead = 0;
3✔
313
                } catch (FileNotFoundException|UnsupportedEncodingException e) {
×
314
                        System.err.println("Not able to read comments from "+currentFilename);
×
315
                }
1✔
316

317
        }
1✔
318

319
        protected String getFileContent( int start, int lenghtToRead) {
320
                char buffer[];
321
                
322
                if(openedFile == null) {
3!
323
                        return "";
×
324
                }
325

326
                buffer = new char[lenghtToRead];
3✔
327
                try {
328
                        
329
                        openedFile.skip(start - lastPositionRead);
9✔
330
                        int ret = openedFile.read( buffer, /*offset in buffer*/0, lenghtToRead);
7✔
331

332
                        if (ret < lenghtToRead) {
3!
333
                                System.err.println("missing bytes in "+ currentFilename + ", read " + ret + " instead of " + lenghtToRead);
×
334
                                return "";
×
335
                        }
336
                        lastPositionRead = start + ret;
5✔
337

338
                        return new String(buffer);
5✔
339
                        
340
                } catch (IOException e) {
×
341
                        e.printStackTrace();
×
342
                }
343
                return "";
×
344
        }
345

346
        protected void closeFile() {
347
                if (openedFile != null) {
3!
348
                        try {
349
                                openedFile.close();
3✔
350
                        } catch (IOException e) {
×
351
                                // nothing
352
                        }
1✔
353
                        openedFile= null;
3✔
354
                }
355
        }
1✔
356

357
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc