View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.coverage.reporting.parsing;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.util.ArrayList;
11  import java.util.List;
12  
13  public final class FileParser {
14      private static final class PendingClass {
15          @NonNull
16          final String className;
17          int braceBalance;
18  
19          PendingClass(@NonNull String className) {
20              this.className = className;
21          }
22      }
23  
24      @NonNull
25      public final LineParser lineParser = new LineParser();
26      @NonNull
27      public final List<PendingClass> currentClasses = new ArrayList<>(2);
28  
29      @Nullable
30      private PendingClass currentClass;
31      private boolean openingBraceForClassFound;
32      private int currentBraceBalance;
33  
34      public boolean parseCurrentLine(@NonNull String line) {
35          if (!lineParser.parse(line)) {
36              return false;
37          }
38  
39          LineElement firstElement = lineParser.getInitialElement();
40          LineElement classDeclaration = findClassNameInNewClassDeclaration();
41  
42          if (classDeclaration != null) {
43              firstElement = classDeclaration;
44              registerStartOfClassDeclaration(classDeclaration);
45          }
46  
47          if (currentClass != null) {
48              detectPotentialEndOfClassDeclaration(firstElement);
49          }
50  
51          return true;
52      }
53  
54      @Nullable
55      private LineElement findClassNameInNewClassDeclaration() {
56          LineElement previous = null;
57  
58          for (LineElement element : lineParser.getInitialElement()) {
59              if (element.isKeyword("class") && (previous == null || !previous.isDotSeparator())) {
60                  return element.getNextCodeElement();
61              }
62  
63              previous = element;
64          }
65  
66          return null;
67      }
68  
69      private void registerStartOfClassDeclaration(@NonNull LineElement elementWithClassName) {
70          String className = elementWithClassName.getText();
71  
72          if (currentClass != null) {
73              currentClass.braceBalance = currentBraceBalance;
74          }
75  
76          currentClass = new PendingClass(className);
77          currentClasses.add(currentClass);
78          currentBraceBalance = 0;
79      }
80  
81      private void detectPotentialEndOfClassDeclaration(@NonNull LineElement firstElement) {
82          // TODO: how to deal with classes defined entirely in one line?
83          currentBraceBalance += firstElement.getBraceBalanceUntilEndOfLine();
84  
85          if (!openingBraceForClassFound && currentBraceBalance > 0) {
86              openingBraceForClassFound = true;
87          } else if (openingBraceForClassFound && currentBraceBalance == 0) {
88              restorePreviousPendingClassIfAny();
89          }
90      }
91  
92      private void restorePreviousPendingClassIfAny() {
93          currentClasses.remove(currentClass);
94  
95          if (currentClasses.isEmpty()) {
96              currentClass = null;
97          } else {
98              currentClass = currentClasses.get(currentClasses.size() - 1);
99              currentBraceBalance = currentClass.braceBalance;
100         }
101     }
102 
103     @Nullable
104     public String getCurrentlyPendingClass() {
105         return currentClass == null ? null : currentClass.className;
106     }
107 }