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