1 package com.stateofflow.eclipse.metrics.calculators;
2
3 import java.util.Stack;
4
5 import org.eclipse.jdt.core.dom.ASTNode;
6 import org.eclipse.jdt.core.dom.ASTVisitor;
7 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
8 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
9 import org.eclipse.jdt.core.dom.EnumDeclaration;
10 import org.eclipse.jdt.core.dom.ITypeBinding;
11 import org.eclipse.jdt.core.dom.MethodDeclaration;
12 import org.eclipse.jdt.core.dom.Modifier;
13 import org.eclipse.jdt.core.dom.TypeDeclaration;
14
15 import com.stateofflow.eclipse.metrics.metric.MetricId;
16 import com.stateofflow.eclipse.metrics.type.AnonymousClassDeclarationAdapter;
17 import com.stateofflow.eclipse.metrics.type.EnumConstantDeclarationAdapter;
18 import com.stateofflow.eclipse.metrics.type.EnumDeclarationAdapter;
19 import com.stateofflow.eclipse.metrics.type.NonNullType;
20 import com.stateofflow.eclipse.metrics.type.TypeDeclarationAdapter;
21
22 public abstract class AbstractASTVisitorCalculator extends ASTVisitor implements Calculator {
23 private final Stack<Memento> enclosingTypeStack = new Stack<Memento>();
24 private NonNullType typeNode;
25 private MethodDeclaration methodNode;
26 private MeasurementContext context;
27
28 private void clearState() {
29 methodNode = null;
30 typeNode = null;
31 }
32
33 @Override
34 public void endVisit(final AnonymousClassDeclaration node) {
35 handleEndOfType();
36 final Memento currentState = getMemento();
37 restoreState(popState());
38 handleAnonymousClass(currentState);
39
40 super.endVisit(node);
41 }
42
43 @Override
44 public void endVisit(final EnumConstantDeclaration node) {
45 endVisitType();
46 super.visit(node);
47 }
48
49 @Override
50 public void endVisit(final EnumDeclaration node) {
51 endVisitType();
52 super.visit(node);
53 }
54
55 @Override
56 public void endVisit(final MethodDeclaration methodDeclarationNode) {
57 methodNode = null;
58 super.endVisit(methodDeclarationNode);
59 }
60
61 @Override
62 public void endVisit(final TypeDeclaration node) {
63 if (node.isInterface() && !isInterfaceCalculator()) {
64 return;
65 }
66
67 endVisitType();
68 super.endVisit(node);
69 }
70
71 private void endVisitType() {
72 handleEndOfType();
73 if (!enclosingTypeStack.isEmpty()) {
74 handleEndOfInnerType();
75 } else {
76 typeNode = null;
77 }
78 }
79
80 protected int getEndLineNumber(final ASTNode node) {
81 return getLineNumber(node.getStartPosition() + node.getLength());
82 }
83
84 protected int getLineNumber(final int position) {
85 return context.getParsedCompilationUnit().getLineNumber(position);
86 }
87
88 private Memento getMemento() {
89 return new Memento(methodNode, typeNode, getRestorableState());
90 }
91
92 protected Object getRestorableState() {
93 return null;
94 }
95
96 protected int getStartLineNumber(final ASTNode node) {
97 return getLineNumber(node.getStartPosition());
98 }
99
100 protected ITypeBinding getTypeNodeBinding() {
101 return typeNode.resolveBinding();
102 }
103
104 protected void handleAnonymousClass(final Memento inner) {
105 handleNestedClass(inner.restorableState);
106 }
107
108 private void handleEndOfInnerType() {
109 final Object currentState = getRestorableState();
110 restoreState(popState());
111 handleNestedClass(currentState);
112 }
113
114 protected void handleEndOfType() {
115 }
116
117 protected void handleNestedClass(@SuppressWarnings("unused") final Object inner) {
118 }
119
120 private void handleStartOfInnerType() {
121 if (isInType()) {
122 pushState();
123 clearState();
124 }
125 }
126
127 protected void handleStartOfType() {
128 }
129
130 protected boolean isConcrete(final MethodDeclaration declaration) {
131 return (declaration.getModifiers() & Modifier.ABSTRACT) == 0;
132 }
133
134 protected boolean isInMethod() {
135 return methodNode != null;
136 }
137
138 protected boolean isInterfaceCalculator() {
139 return false;
140 }
141
142 private boolean isInType() {
143 return typeNode != null;
144 }
145
146 public void measure(final MeasurementContext context) {
147 this.context = context;
148 context.getParsedCompilationUnit().accept(this);
149 this.context = null;
150 }
151
152 protected void noteMethodValue(final MetricId metricId, final int value) {
153 context.noteMethodValue(metricId, value, typeNode, methodNode);
154 }
155
156 protected void noteTypeValue(final MetricId metricId, final int value) {
157 context.noteTypeValue(metricId, value, typeNode);
158 }
159
160 private Memento popState() {
161 return enclosingTypeStack.pop();
162 }
163
164 private void pushState() {
165 enclosingTypeStack.push(getMemento());
166 }
167
168 protected void restoreSpecificState(@SuppressWarnings("unused") final Object restorableState) {
169 }
170
171 protected final void restoreState(final Memento memento) {
172 methodNode = memento.methodNode;
173 typeNode = memento.typeNode;
174 restoreSpecificState(memento.restorableState);
175 }
176
177 @Override
178 public boolean visit(final AnonymousClassDeclaration node) {
179 pushState();
180 clearState();
181 typeNode = new AnonymousClassDeclarationAdapter(node);
182
183 handleStartOfType();
184 return super.visit(node);
185 }
186
187 @Override
188 public boolean visit(final EnumConstantDeclaration node) {
189 visitType(new EnumConstantDeclarationAdapter(node));
190 return super.visit(node);
191 }
192
193 @Override
194 public boolean visit(final EnumDeclaration node) {
195 visitType(new EnumDeclarationAdapter(node));
196 return super.visit(node);
197 }
198
199 @Override
200 public boolean visit(final MethodDeclaration methodDeclarationNode) {
201 methodNode = methodDeclarationNode;
202 return super.visit(methodDeclarationNode);
203 }
204
205 @Override
206 public boolean visit(final TypeDeclaration node) {
207 if (node.isInterface() && !isInterfaceCalculator()) {
208 return false;
209 }
210
211 visitType(new TypeDeclarationAdapter(node));
212 return super.visit(node);
213 }
214
215 private void visitType(final NonNullType node) {
216 handleStartOfInnerType();
217
218 typeNode = node;
219 handleStartOfType();
220 }
221 }
222