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