1   package com.stateofflow.eclipse.metrics.calculators.linesofcode;
2   
3   import java.util.Stack;
4   
5   import org.eclipse.jdt.core.dom.ASTNode;
6   import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
7   import org.eclipse.jdt.core.dom.ArrayAccess;
8   import org.eclipse.jdt.core.dom.AssertStatement;
9   import org.eclipse.jdt.core.dom.Assignment;
10  import org.eclipse.jdt.core.dom.Block;
11  import org.eclipse.jdt.core.dom.BooleanLiteral;
12  import org.eclipse.jdt.core.dom.BreakStatement;
13  import org.eclipse.jdt.core.dom.CastExpression;
14  import org.eclipse.jdt.core.dom.CatchClause;
15  import org.eclipse.jdt.core.dom.CharacterLiteral;
16  import org.eclipse.jdt.core.dom.ClassInstanceCreation;
17  import org.eclipse.jdt.core.dom.ConditionalExpression;
18  import org.eclipse.jdt.core.dom.ConstructorInvocation;
19  import org.eclipse.jdt.core.dom.ContinueStatement;
20  import org.eclipse.jdt.core.dom.DoStatement;
21  import org.eclipse.jdt.core.dom.EnhancedForStatement;
22  import org.eclipse.jdt.core.dom.ExpressionStatement;
23  import org.eclipse.jdt.core.dom.ForStatement;
24  import org.eclipse.jdt.core.dom.IfStatement;
25  import org.eclipse.jdt.core.dom.InfixExpression;
26  import org.eclipse.jdt.core.dom.InstanceofExpression;
27  import org.eclipse.jdt.core.dom.Javadoc;
28  import org.eclipse.jdt.core.dom.LabeledStatement;
29  import org.eclipse.jdt.core.dom.MarkerAnnotation;
30  import org.eclipse.jdt.core.dom.MethodDeclaration;
31  import org.eclipse.jdt.core.dom.Modifier;
32  import org.eclipse.jdt.core.dom.NormalAnnotation;
33  import org.eclipse.jdt.core.dom.NullLiteral;
34  import org.eclipse.jdt.core.dom.NumberLiteral;
35  import org.eclipse.jdt.core.dom.ParenthesizedExpression;
36  import org.eclipse.jdt.core.dom.PrimitiveType;
37  import org.eclipse.jdt.core.dom.QualifiedName;
38  import org.eclipse.jdt.core.dom.QualifiedType;
39  import org.eclipse.jdt.core.dom.ReturnStatement;
40  import org.eclipse.jdt.core.dom.SimpleName;
41  import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
42  import org.eclipse.jdt.core.dom.StringLiteral;
43  import org.eclipse.jdt.core.dom.SwitchCase;
44  import org.eclipse.jdt.core.dom.SwitchStatement;
45  import org.eclipse.jdt.core.dom.SynchronizedStatement;
46  import org.eclipse.jdt.core.dom.ThisExpression;
47  import org.eclipse.jdt.core.dom.ThrowStatement;
48  import org.eclipse.jdt.core.dom.TryStatement;
49  import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
50  import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
51  
52  import com.stateofflow.eclipse.metrics.calculators.AbstractASTVisitorCalculator;
53  import com.stateofflow.eclipse.metrics.configuration.MetricsConfiguration;
54  import com.stateofflow.eclipse.metrics.metric.MetricId;
55  import com.stateofflow.eclipse.metrics.metric.MetricPropertyKey;
56  
57  public final class LinesOfCodeCalculator extends AbstractASTVisitorCalculator {
58      public static final MetricId METRIC_ID = MetricId.createMethodId(LinesOfCodeCalculator.class);
59      public static final MetricPropertyKey INCLUDE_METHOD_COMMENTS_KEY = new MetricPropertyKey(METRIC_ID, "includeComments");
60      public static final MetricPropertyKey IGNORE_COMMENTS_BLANKS_KEY = new MetricPropertyKey(METRIC_ID, "ignoreBlanks");
61  
62      private final MetricsConfiguration configuration;
63      private final LinesOfCode linesOfCode = new LinesOfCode();
64      private final Stack<Integer> nestedClassesLinesOfCode = new Stack<Integer>();
65  
66      public LinesOfCodeCalculator(final MetricsConfiguration configuration) {
67          this.configuration = configuration;
68      }
69  
70      private void checkAndIncrementLocCount(final ASTNode node) {
71          linesOfCode.checkAndIncrement(getStartLineNumber(node));
72      }
73  
74      private boolean checkAndRecurse(final ASTNode node) {
75          checkAndIncrementLocCount(node);
76          return getStartLineNumber(node) != getEndLineNumber(node);
77      }
78  
79      private void measure(final ASTNode startNode, final ASTNode endNode) {
80          noteMethodValue(LinesOfCodeCalculator.METRIC_ID, getEndLineNumber(endNode) - getLineNumber(startNode.getStartPosition()) + 1);
81      }
82  
83      private void measureConcreteMethod(final MethodDeclaration declaration) {
84          if (configuration.getBoolean(INCLUDE_METHOD_COMMENTS_KEY)) {
85              measureFromStartOfJavaDoc(declaration);
86          } else {
87              measureExcludingJavaDoc(declaration);
88          }
89          noteMethodValue(LinesOfCodeCalculator.METRIC_ID, linesOfCode.getCount());
90      }
91  
92      private void measureExcludingJavaDoc(final MethodDeclaration declaration) {
93          if (declaration.getBody() != null) {
94              measureMethodBody(declaration);
95          } else {
96              measureNative();
97          }
98      }
99  
100     private void measureNative() {
101         linesOfCode.initialise(0);
102         linesOfCode.setCount(1);
103     }
104 
105     private void measureMethodBody(final MethodDeclaration declaration) {
106         linesOfCode.initialise(getStartLineNumber(declaration.getBody()));
107         declaration.getBody().accept(this);
108     }
109 
110     private void measureFromStartOfJavaDoc(final MethodDeclaration declaration) {
111         linesOfCode.initialise(getStartLineNumber(declaration));
112         if (declaration.getJavadoc() != null) {
113             declaration.getJavadoc().accept(this);
114         }
115     }
116 
117     private void measureSimpleMetric(final MethodDeclaration declaration) {
118         if (configuration.getBoolean(INCLUDE_METHOD_COMMENTS_KEY)) {
119             measure(declaration, declaration);
120         } else {
121             measure(declaration.getName(), declaration);
122         }
123     }
124 
125     @Override
126     public boolean visit(final AnonymousClassDeclaration node) {
127         nestedClassesLinesOfCode.push(new Integer(linesOfCode.getCount()));
128         linesOfCode.setCount(0);
129         return super.visit(node);
130     }
131 
132     @Override
133     public void endVisit(final AnonymousClassDeclaration node) {
134         linesOfCode.setCount((nestedClassesLinesOfCode.pop()).intValue());
135         super.endVisit(node);
136     }
137 
138     @Override
139     public boolean visit(final ArrayAccess node) {
140         return checkAndRecurse(node);
141     }
142 
143     @Override
144     public boolean visit(final AssertStatement node) {
145         return checkAndRecurse(node);
146     }
147 
148     @Override
149     public boolean visit(final Assignment node) {
150         return checkAndRecurse(node);
151     }
152 
153     @Override
154     public boolean visit(final Block node) {
155         return true;
156     }
157 
158     @Override
159     public boolean visit(final BooleanLiteral node) {
160         return checkAndRecurse(node);
161     }
162 
163     @Override
164     public boolean visit(final BreakStatement node) {
165         return checkAndRecurse(node);
166     }
167 
168     @Override
169     public boolean visit(final CastExpression node) {
170         return checkAndRecurse(node);
171     }
172 
173     @Override
174     public boolean visit(final CatchClause node) {
175         return checkAndRecurse(node);
176     }
177 
178     @Override
179     public boolean visit(final CharacterLiteral node) {
180         return checkAndRecurse(node);
181     }
182 
183     @Override
184     public boolean visit(final ClassInstanceCreation node) {
185         return checkAndRecurse(node);
186     }
187 
188     @Override
189     public boolean visit(final ConditionalExpression node) {
190         return checkAndRecurse(node);
191     }
192 
193     @Override
194     public boolean visit(final ConstructorInvocation node) {
195         return checkAndRecurse(node);
196     }
197 
198     @Override
199     public boolean visit(final ContinueStatement node) {
200         return checkAndRecurse(node);
201     }
202 
203     @Override
204     public boolean visit(final DoStatement node) {
205         return checkAndRecurse(node);
206     }
207 
208     @Override
209     public boolean visit(final EnhancedForStatement node) {
210         return checkAndRecurse(node);
211     }
212 
213     @Override
214     public boolean visit(final ExpressionStatement node) {
215         return checkAndRecurse(node);
216     }
217 
218     @Override
219     public boolean visit(final ForStatement node) {
220         return checkAndRecurse(node);
221     }
222 
223     @Override
224     public boolean visit(final IfStatement node) {
225         return checkAndRecurse(node);
226     }
227 
228     @Override
229     public boolean visit(final InfixExpression node) {
230         return checkAndRecurse(node);
231     }
232 
233     @Override
234     public boolean visit(final InstanceofExpression node) {
235         return checkAndRecurse(node);
236     }
237 
238     @Override
239     public boolean visit(final Javadoc node) {
240         linesOfCode.increment(getEndLineNumber(node) - getStartLineNumber(node) + 1);
241         return false;
242     }
243 
244     @Override
245     public boolean visit(final LabeledStatement node) {
246         return checkAndRecurse(node);
247     }
248 
249     @Override
250     public boolean visit(final MarkerAnnotation node) {
251         return checkAndRecurse(node);
252     }
253 
254     @Override
255     public boolean visit(final MethodDeclaration arg0) {
256         super.visit(arg0);
257         if (isConcrete(arg0)) {
258             if (configuration.getBoolean(IGNORE_COMMENTS_BLANKS_KEY)) {
259                 measureConcreteMethod(arg0);
260             } else {
261                 measureSimpleMetric(arg0);
262             }
263         }
264         return false;
265     }
266 
267     @Override
268     public boolean visit(final Modifier node) {
269         return checkAndRecurse(node);
270     }
271 
272     @Override
273     public boolean visit(final NormalAnnotation node) {
274         return checkAndRecurse(node);
275     }
276 
277     @Override
278     public boolean visit(final NullLiteral node) {
279         return checkAndRecurse(node);
280     }
281 
282     @Override
283     public boolean visit(final NumberLiteral node) {
284         return checkAndRecurse(node);
285     }
286 
287     @Override
288     public boolean visit(final ParenthesizedExpression node) {
289         return true;
290     }
291 
292     @Override
293     public boolean visit(final PrimitiveType node) {
294         return checkAndRecurse(node);
295     }
296 
297     @Override
298     public boolean visit(final QualifiedName node) {
299         return checkAndRecurse(node);
300     }
301 
302     @Override
303     public boolean visit(final QualifiedType node) {
304         return checkAndRecurse(node);
305     }
306 
307     @Override
308     public boolean visit(final ReturnStatement node) {
309         return checkAndRecurse(node);
310     }
311 
312     @Override
313     public boolean visit(final SimpleName node) {
314         return checkAndRecurse(node);
315     }
316 
317     @Override
318     public boolean visit(final SingleMemberAnnotation node) {
319         return checkAndRecurse(node);
320     }
321 
322     @Override
323     public boolean visit(final StringLiteral node) {
324         return checkAndRecurse(node);
325     }
326 
327     @Override
328     public boolean visit(final SwitchCase node) {
329         return checkAndRecurse(node);
330     }
331 
332     @Override
333     public boolean visit(final SwitchStatement node) {
334         return checkAndRecurse(node);
335     }
336 
337     @Override
338     public boolean visit(final SynchronizedStatement node) {
339         return checkAndRecurse(node);
340     }
341 
342     @Override
343     public boolean visit(final ThisExpression node) {
344         return checkAndRecurse(node);
345     }
346 
347     @Override
348     public boolean visit(final ThrowStatement node) {
349         return checkAndRecurse(node);
350     }
351 
352     @Override
353     public boolean visit(final TryStatement node) {
354         return checkAndRecurse(node);
355     }
356 
357     @Override
358     public boolean visit(final VariableDeclarationFragment node) {
359         return checkAndRecurse(node);
360     }
361 
362     @Override
363     public boolean visit(final VariableDeclarationStatement node) {
364         return checkAndRecurse(node);
365     }
366 
367 }
368