summaryrefslogtreecommitdiff
path: root/plugins/coverage/src/com/intellij/coverage/view/JavaCoverageViewExtension.java
blob: 5a7034e1b08eaca5f5d7749147d66fd3a33d71be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
package com.intellij.coverage.view;

import com.intellij.coverage.*;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ui.ColumnInfo;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * User: anna
 * Date: 1/5/12
 */
public class JavaCoverageViewExtension extends CoverageViewExtension {
  private final JavaCoverageAnnotator myAnnotator;

  public JavaCoverageViewExtension(JavaCoverageAnnotator annotator,
                                   Project project,
                                   CoverageSuitesBundle suitesBundle,
                                   CoverageViewManager.StateBean stateBean) {
    super(project, suitesBundle, stateBean);
    myAnnotator = annotator;
  }

  @Override
  public String getSummaryForNode(AbstractTreeNode node) {
    final String coverageInformationString = myAnnotator
      .getPackageCoverageInformationString((PsiPackage)node.getValue(), null, myCoverageDataManager, myStateBean.myFlattenPackages);
    return "Coverage Summary for Package \'" + node.toString() + "\': " + getNotCoveredMessage(coverageInformationString);
  }

  @Override
  public String getSummaryForRootNode(AbstractTreeNode childNode) {
    final Object value = childNode.getValue();
    String coverageInformationString = myAnnotator.getPackageCoverageInformationString((PsiPackage)value, null,
                                                                                       myCoverageDataManager);
    if (coverageInformationString == null) {
      if (!myCoverageViewManager.isReady()) return "Loading...";
      PackageAnnotator.PackageCoverageInfo info = new PackageAnnotator.PackageCoverageInfo();
      final Collection children = childNode.getChildren();
      for (Object child : children) {
        final Object childValue = ((CoverageListNode)child).getValue();
        if (childValue instanceof PsiPackage) {
          final PackageAnnotator.PackageCoverageInfo coverageInfo = myAnnotator.getPackageCoverageInfo((PsiPackage)childValue, myStateBean.myFlattenPackages);
          if (coverageInfo != null) {
            info = JavaCoverageAnnotator.merge(info, coverageInfo);
          }
        } else {
          final PackageAnnotator.ClassCoverageInfo classCoverageInfo = getClassCoverageInfo(((PsiClass)childValue));
          if (classCoverageInfo != null) {
            info.coveredClassCount += classCoverageInfo.coveredMethodCount > 0 ? 1 : 0;
            info.totalClassCount ++;

            info.coveredMethodCount += classCoverageInfo.coveredMethodCount;
            info.totalMethodCount += classCoverageInfo.totalMethodCount;

            info.coveredLineCount += classCoverageInfo.partiallyCoveredLineCount + classCoverageInfo.fullyCoveredLineCount;
            info.totalLineCount += classCoverageInfo.totalLineCount;
          }
        }
      }
      coverageInformationString = JavaCoverageAnnotator.getCoverageInformationString(info, false);
    }
    return "Coverage Summary for \'all classes in scope\': " + getNotCoveredMessage(coverageInformationString);
  }

  private static String getNotCoveredMessage(String coverageInformationString) {
    if (coverageInformationString == null) {
      coverageInformationString = "not covered";
    }
    return coverageInformationString;
  }

  @Override
  public String getPercentage(int columnIndex, AbstractTreeNode node) {
    final Object value = node.getValue();
    if (value instanceof PsiClass) {

      //no coverage gathered
      if (((PsiClass)value).isInterface()) return null;

      final String qualifiedName = ((PsiClass)value).getQualifiedName();
      if (columnIndex == 1) {
        return myAnnotator.getClassCoveredPercentage(qualifiedName);
      } else if (columnIndex == 2){
        return myAnnotator.getClassMethodPercentage(qualifiedName);
      }
      
      return myAnnotator.getClassLinePercentage(qualifiedName);
    }
    if (value instanceof PsiPackage) {
      final boolean flatten = myStateBean.myFlattenPackages;
      if (columnIndex == 1) {
        return myAnnotator.getPackageClassPercentage((PsiPackage)value, flatten);
      } else if (columnIndex == 2) {
        return myAnnotator.getPackageMethodPercentage((PsiPackage)value, flatten);
      }
      return myAnnotator.getPackageLinePercentage((PsiPackage)value, flatten);
    }
    return null;
  }

  @Override
  public PsiElement getElementToSelect(Object object) {
    PsiElement psiElement = super.getElementToSelect(object);
    if (psiElement != null) {
      final PsiFile containingFile = psiElement.getContainingFile();
      if (containingFile instanceof PsiClassOwner) {
        final PsiClass[] classes = ((PsiClassOwner)containingFile).getClasses();
        if (classes.length == 1) return classes[0];
        for (PsiClass aClass : classes) {
          if (PsiTreeUtil.isAncestor(aClass, psiElement, false)) return aClass;
        }
      }
    }
    return psiElement;
  }

  @Override
  public VirtualFile getVirtualFile(Object object) {
    if (object instanceof PsiPackage) {
      final PsiDirectory[] directories = ((PsiPackage)object).getDirectories();
      return directories.length > 0 ? directories[0].getVirtualFile() : null;
    }
    return super.getVirtualFile(object);
  }

  @Nullable
  @Override
  public PsiElement getParentElement(PsiElement element) {
    if (element instanceof PsiClass) {
      final PsiDirectory containingDirectory = element.getContainingFile().getContainingDirectory();
      return containingDirectory != null ? JavaDirectoryService.getInstance().getPackage(containingDirectory) : null;
    }
    return ((PsiPackage)element).getParentPackage();
  }

  @Override
  public AbstractTreeNode createRootNode() {
    return new CoverageListRootNode(myProject, JavaPsiFacade.getInstance(myProject).findPackage(""), mySuitesBundle, myStateBean);
  }

  @Override
  public List<AbstractTreeNode> createTopLevelNodes() {
    final List<AbstractTreeNode> topLevelNodes = new ArrayList<AbstractTreeNode>();
    final LinkedHashSet<PsiPackage> packages = new LinkedHashSet<PsiPackage>();
    final LinkedHashSet<PsiClass> classes = new LinkedHashSet<PsiClass>();
    for (CoverageSuite suite : mySuitesBundle.getSuites()) {
      packages.addAll(((JavaCoverageSuite)suite).getCurrentSuitePackages(myProject));
      classes.addAll(((JavaCoverageSuite)suite).getCurrentSuiteClasses(myProject));
    }

    final Set<PsiPackage> packs = new HashSet<PsiPackage>();
    for (PsiPackage aPackage : packages) {
      final String qualifiedName = aPackage.getQualifiedName();
      for (PsiPackage psiPackage : packages) {
        if (psiPackage.getQualifiedName().startsWith(qualifiedName + ".")) {
          packs.add(psiPackage);
          break;
        }
      }
    }
    packages.removeAll(packs);

    for (PsiPackage aPackage : packages) {
      final GlobalSearchScope searchScope = mySuitesBundle.getSearchScope(myProject);
      if (aPackage.getDirectories(searchScope).length == 0) continue;
      if (aPackage.getClasses(searchScope).length != 0) {
        final CoverageListNode node = new CoverageListNode(myProject, aPackage, mySuitesBundle, myStateBean);
        topLevelNodes.add(node);
      }
      collectSubPackages(topLevelNodes, aPackage, mySuitesBundle, myStateBean);
    }

    for (PsiClass aClass : classes) {
      if (getClassCoverageInfo(aClass) == null) continue;
      topLevelNodes.add(new CoverageListNode(myProject, aClass, mySuitesBundle, myStateBean));
    }
    return topLevelNodes;
  }

  private static void collectSubPackages(List<AbstractTreeNode> children,
                                         final PsiPackage rootPackage,
                                         final CoverageSuitesBundle data,
                                         final CoverageViewManager.StateBean stateBean) {
    final GlobalSearchScope searchScope = data.getSearchScope(rootPackage.getProject());
    final PsiPackage[] subPackages = ApplicationManager.getApplication().runReadAction(new Computable<PsiPackage[]>() {
      public PsiPackage[] compute() {
        return rootPackage.getSubPackages(searchScope); 
      }
    });
    for (final PsiPackage aPackage : subPackages) {
      final PsiDirectory[] directories = ApplicationManager.getApplication().runReadAction(new Computable<PsiDirectory[]>() {
        public PsiDirectory[] compute() {
          return aPackage.getDirectories(searchScope); 
        }
      });
      if (directories.length == 0 && !ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
        public Boolean compute() {
          return JavaPsiFacade.getInstance(aPackage.getProject()).isPartOfPackagePrefix(aPackage.getQualifiedName());
        }
      })) continue;
      if (ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
        public Boolean compute() {
          return isInCoverageScope(aPackage, data);
        }
      })) {
        final CoverageListNode node = new CoverageListNode(rootPackage.getProject(), aPackage, data, stateBean);
        children.add(node);
      }
      else if (!stateBean.myFlattenPackages) {
        collectSubPackages(children, aPackage, data, stateBean);
      }
      if (stateBean.myFlattenPackages) {
        collectSubPackages(children, aPackage, data, stateBean);
      }
    }
  }


  @Override
  public List<AbstractTreeNode> getChildrenNodes(final AbstractTreeNode node) {
    List<AbstractTreeNode> children = new ArrayList<AbstractTreeNode>();
    if (node instanceof CoverageListNode) {
      final Object val = node.getValue();
      if (val instanceof PsiClass) return Collections.emptyList();

      //append package classes
      if (val instanceof PsiPackage) {
        if (!myStateBean.myFlattenPackages) {
          collectSubPackages(children, (PsiPackage)val, mySuitesBundle, myStateBean);
        }
        if (ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
          public Boolean compute() {
            return isInCoverageScope((PsiPackage)val, mySuitesBundle);
          }
        })) {
          final PsiClass[] classes = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass[]>() {
            public PsiClass[] compute() {
              return ((PsiPackage)val).getClasses(mySuitesBundle.getSearchScope(node.getProject()));
            }
          });
          for (PsiClass aClass : classes) {
            if (!(node instanceof CoverageListRootNode) && getClassCoverageInfo(aClass) == null) continue;
            children.add(new CoverageListNode(myProject, aClass, mySuitesBundle, myStateBean));
          }
        }
      }
      if (node instanceof CoverageListRootNode) {
        for (CoverageSuite suite : mySuitesBundle.getSuites()) {
          final List<PsiClass> classes = ((JavaCoverageSuite)suite).getCurrentSuiteClasses(myProject);
          for (PsiClass aClass : classes) {
            children.add(new CoverageListNode(myProject, aClass, mySuitesBundle, myStateBean));
          }
        }
      }
      for (AbstractTreeNode childNode : children) {
        childNode.setParent(node);
      }
    }
    return children;
  }

  @Nullable
  private PackageAnnotator.ClassCoverageInfo getClassCoverageInfo(final PsiClass aClass) {
    return myAnnotator.getClassCoverageInfo(ApplicationManager.getApplication().runReadAction(new NullableComputable<String>() {
      public String compute() {
        return aClass.getQualifiedName();
      }
    }));
  }

  @Override
  public ColumnInfo[] createColumnInfos() {
    return new ColumnInfo[]{
      new ElementColumnInfo(), 
      new PercentageCoverageColumnInfo(1, "Class, %", mySuitesBundle, myStateBean), 
      new PercentageCoverageColumnInfo(2, "Method, %", mySuitesBundle, myStateBean),
      new PercentageCoverageColumnInfo(3, "Line, %", mySuitesBundle, myStateBean)
    };
  }

  private static boolean isInCoverageScope(PsiElement element, CoverageSuitesBundle suitesBundle) {
    if (element instanceof PsiPackage) {
      final PsiPackage psiPackage = (PsiPackage)element;
      final String qualifiedName = psiPackage.getQualifiedName();
      for (CoverageSuite suite : suitesBundle.getSuites()) {
        if (((JavaCoverageSuite)suite).isPackageFiltered(qualifiedName)) return true;
      }
    }
    return false;
  }

  @Override
  public boolean canSelectInCoverageView(Object object) {
    final PsiFile psiFile = object instanceof VirtualFile ? PsiManager.getInstance(myProject).findFile((VirtualFile)object) : null;
    if (psiFile instanceof PsiClassOwner) {
      final String packageName = ((PsiClassOwner)psiFile).getPackageName();
      return isInCoverageScope(JavaPsiFacade.getInstance(myProject).findPackage(packageName), mySuitesBundle);
    }
    if (object instanceof PsiPackage) {
      return isInCoverageScope((PsiElement)object, mySuitesBundle);
    }
    return false;
  }

  @Override
  public boolean supportFlattenPackages() {
    return true;
  }
}