From b355f1a11bd712bef3dc3023ca8a195c591567e5 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Thu, 4 Oct 2018 19:54:55 +0300 Subject: [PATCH] streaming write of inspection results (from toolwindow only) --- .../UnusedDeclarationPresentation.java | 8 +- .../codeInspection/reference/RefManager.java | 13 ++- .../reference/RefManagerImpl.java | 4 +- .../ex/GlobalInspectionContextImpl.java | 2 +- .../ui/DefaultInspectionToolPresentation.java | 16 ++-- .../ui/InspectionToolPresentation.java | 9 +- .../ui/actions/ExportHTMLAction.java | 92 +++++++++++++++---- .../testFramework/InspectionTestUtil.java | 2 +- 8 files changed, 112 insertions(+), 34 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java index 65347f9f8f46..d66155840654 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java @@ -63,6 +63,7 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.net.URL; import java.util.*; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -155,7 +156,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta } @Override - public void exportResults(@NotNull final Element parentNode, + public void exportResults(@NotNull Consumer problemSink, @NotNull RefEntity refEntity, @NotNull Predicate excludedDescriptions) { if (!(refEntity instanceof RefJavaElement)) return; @@ -167,7 +168,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta if (!compareVisibilities(refElement, getTool().getSharedLocalInspectionTool())) return; if (skipEntryPoints(refElement)) return; - Element element = refEntity.getRefManager().export(refEntity, parentNode, -1); + Element element = refEntity.getRefManager().export(refEntity, -1); if (element == null) return; @NonNls Element problemClassElement = new Element(InspectionsBundle.message("inspection.export.results.problem.element.tag")); @@ -194,8 +195,9 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta DeadHTMLComposer.appendProblemSynopsis((RefElement)refEntity, buf); descriptionElement.addContent(buf.toString()); element.addContent(descriptionElement); + problemSink.accept(element); } - super.exportResults(parentNode, refEntity, excludedDescriptions); + super.exportResults(problemSink, refEntity, excludedDescriptions); } @NotNull diff --git a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefManager.java b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefManager.java index aea2b2e2e273..f051c0d7a013 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefManager.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefManager.java @@ -95,7 +95,18 @@ public abstract class RefManager { @NotNull public abstract RefEntity getRefinedElement(@NotNull RefEntity ref); - public abstract Element export(@NotNull RefEntity entity, @NotNull Element element, final int actualLine); + @Nullable + public Element export(@NotNull RefEntity entity, @NotNull Element parent, final int actualLine) { + Element element = export(entity, actualLine); + if (element == null) return null; + parent.addContent(element); + return element; + } + + @Nullable + public Element export(@NotNull RefEntity entity, final int actualLine) { + throw new UnsupportedOperationException(); + } @Nullable public abstract String getGroupName(@NotNull RefElement entity); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index f4ef78c4aec2..31b5ee7d013f 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -245,8 +245,9 @@ public class RefManagerImpl extends RefManager { return ref; } + @Nullable @Override - public Element export(@NotNull RefEntity refEntity, @NotNull final Element element, final int actualLine) { + public Element export(@NotNull RefEntity refEntity, final int actualLine) { refEntity = getRefinedElement(refEntity); Element problem = new Element("problem"); @@ -298,7 +299,6 @@ public class RefManagerImpl extends RefManager { } new SmartRefElementPointerImpl(refEntity, true).writeExternal(problem); - element.addContent(problem); return problem; } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 46bd90aa1887..fffb45d3e87e 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -241,7 +241,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp try { InspectionToolWrapper toolWrapper = state.getTool(); InspectionToolPresentation presentation = getPresentation(toolWrapper); - presentation.exportResults(element, refEntity, d -> false); + presentation.exportResults(e -> element.addContent(e), refEntity, d -> false); } catch (Throwable e) { LOG.error("Problem when exporting: " + refEntity.getExternalName(), e); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java index ccf993a4f86b..e53a0a1673b7 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.*; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -206,14 +207,14 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta } @Override - public void exportResults(@NotNull final Element parentNode, + public void exportResults(@NotNull final Consumer problemSink, @NotNull final Predicate excludedEntities, @NotNull final Predicate excludedDescriptors) { getRefManager().iterate(new RefVisitor(){ @Override public void visitElement(@NotNull RefEntity elem) { if (!excludedEntities.test(elem)) { - exportResults(parentNode, elem, excludedDescriptors); + exportResults(problemSink, elem, excludedDescriptors); } } }); @@ -260,7 +261,7 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta private synchronized void writeOutput(@NotNull final CommonProblemDescriptor[] descriptions, @NotNull RefEntity refElement) { final Element parentNode = new Element(InspectionsBundle.message("inspection.problems")); - exportResults(descriptions, refElement, parentNode, d -> false); + exportResults(descriptions, refElement, p -> parentNode.addContent(p), d -> false); final List list = parentNode.getChildren(); @NonNls final String ext = ".xml"; @@ -327,25 +328,26 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta } @Override - public void exportResults(@NotNull final Element parentNode, + public void exportResults(@NotNull Consumer problemSink, @NotNull RefEntity refEntity, @NotNull Predicate isDescriptorExcluded) { CommonProblemDescriptor[] descriptions = getProblemElements().get(refEntity); if (descriptions != null) { - exportResults(descriptions, refEntity, parentNode, isDescriptorExcluded); + exportResults(descriptions, refEntity, problemSink, isDescriptorExcluded); } } private void exportResults(@NotNull final CommonProblemDescriptor[] descriptors, @NotNull RefEntity refEntity, - @NotNull Element parentNode, + @NotNull Consumer problemSink, @NotNull Predicate isDescriptorExcluded) { for (CommonProblemDescriptor descriptor : descriptors) { if (isDescriptorExcluded.test(descriptor)) continue; int line = descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getLineNumber() : -1; - Element element = refEntity.getRefManager().export(refEntity, parentNode, line); + Element element = refEntity.getRefManager().export(refEntity, line); if (element == null) return; exportResult(refEntity, descriptor, element); + problemSink.accept(element); } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java index fc6e44cfb278..4d7df2d4ec9a 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java @@ -25,6 +25,7 @@ import javax.swing.*; import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; public interface InspectionToolPresentation extends ProblemDescriptionsProcessor { @@ -80,7 +81,7 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor IntentionAction findQuickFixes(@NotNull CommonProblemDescriptor descriptor, final String hint); @NotNull HTMLComposerImpl getComposer(); - void exportResults(@NotNull final Element parentNode, @NotNull RefEntity refEntity, Predicate isDescriptorExcluded); + @NotNull QuickFixAction[] getQuickFixes(@NotNull RefEntity... refElements); @NotNull @@ -92,7 +93,11 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull GlobalInspectionContextImpl getContext(); - void exportResults(@NotNull Element parentNode, + void exportResults(@NotNull Consumer problemSink, + @NotNull RefEntity refEntity, + @NotNull Predicate isDescriptorExcluded); + + void exportResults(@NotNull Consumer problemSink, @NotNull Predicate isEntityExcluded, @NotNull Predicate isProblemExcluded); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java index bcace50b990f..a71f95a80a85 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/ExportHTMLAction.java @@ -23,6 +23,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.PathMacroManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbAware; @@ -33,19 +34,26 @@ import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.util.ui.tree.TreeUtil; import gnu.trove.THashSet; import org.jdom.Document; import org.jdom.Element; +import org.jdom.output.Format; +import org.jdom.output.StAXStreamOutputter; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.io.File; -import java.io.IOException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.*; import java.util.HashSet; import java.util.Set; public class ExportHTMLAction extends AnAction implements DumbAware { + private static final Logger LOG = Logger.getInstance(ExportHTMLAction.class); private final InspectionResultsView myView; @NonNls private static final String PROBLEMS = "problems"; @NonNls private static final String ROOT = "root"; @@ -119,32 +127,75 @@ public class ExportHTMLAction extends AnAction implements DumbAware { throw new IOException("Cannot create \'" + outputDir + "\'"); } final InspectionTreeNode root = myView.getTree().getRoot(); - final IOException[] ex = new IOException[1]; + final Exception[] ex = new Exception[1]; final Set visitedWrappers = new THashSet<>(); final Element aggregateRoot = new Element(ROOT); + + Format format = JDOMUtil.createFormat("\n"); + String initialSeparator = format.getLineSeparator(); + format.setLineSeparator(initialSeparator + format.getIndent()); + StAXStreamOutputter streamOutputter = new StAXStreamOutputter(format); + XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); + TreeUtil.treeNodeTraverser(root).traverse().processEach(node -> { if (node instanceof InspectionNode) { InspectionNode toolNode = (InspectionNode)node; - Element problems = new Element(PROBLEMS); + InspectionToolWrapper toolWrapper = toolNode.getToolWrapper(); if (!visitedWrappers.add(toolWrapper)) return true; - final Set toolWrappers = getWorkedTools(toolNode); - for (InspectionToolWrapper wrapper : toolWrappers) { - InspectionToolPresentation presentation = myView.getGlobalInspectionContext().getPresentation(wrapper); - if (!toolNode.isExcluded()) { - presentation.exportResults(problems, presentation::isExcluded, presentation::isExcluded); - presentation.exportAggregateResults(aggregateRoot, presentation::isExcluded, presentation::isExcluded); - } - } - PathMacroManager.getInstance(myView.getProject()).collapsePaths(problems); + String name = toolWrapper.getShortName(); + XMLStreamWriter xmlWriter = null; + BufferedWriter fileWriter = null; try { - writeDocument(problems, outputDirectoryName, toolWrapper.getShortName()); - writeDocument(aggregateRoot, outputDirectoryName, toolWrapper.getShortName() + AGGREGATE); + fileWriter = getWriter(outputDirectoryName, name); + xmlWriter = xmlOutputFactory.createXMLStreamWriter(fileWriter); + xmlWriter.writeStartElement(PROBLEMS); + + + final Set toolWrappers = getWorkedTools(toolNode); + for (InspectionToolWrapper wrapper : toolWrappers) { + InspectionToolPresentation presentation = myView.getGlobalInspectionContext().getPresentation(wrapper); + if (!toolNode.isExcluded()) { + XMLStreamWriter finalReportWriter = xmlWriter; + presentation.exportResults(p -> { + + PathMacroManager.getInstance(myView.getProject()).collapsePaths(p); + try { + finalReportWriter.writeCharacters(format.getLineSeparator()); + streamOutputter.output(p, finalReportWriter); + } + catch (XMLStreamException e) { + throw new RuntimeException(e); + } + }, presentation::isExcluded, presentation::isExcluded); + presentation.exportAggregateResults(aggregateRoot, presentation::isExcluded, presentation::isExcluded); + } + } + writeDocument(aggregateRoot, outputDirectoryName, name + AGGREGATE); + xmlWriter.writeCharacters(initialSeparator); + xmlWriter.writeEndElement(); } - catch (IOException e) { + catch (Exception e) { ex[0] = e; + } finally { + try { + if (xmlWriter != null) { + xmlWriter.flush(); + } + } + catch (XMLStreamException e) { + LOG.error(e); + } + if (fileWriter != null) { + try { + fileWriter.close(); + } + catch (IOException e) { + if (ex[0] != null) ex[0] = e; + } + } } } return true; @@ -161,11 +212,18 @@ public class ExportHTMLAction extends AnAction implements DumbAware { new File(outputDirectoryName, InspectionApplication.DESCRIPTIONS + InspectionApplication.XML_EXTENSION), CodeStyle.getDefaultSettings().getLineSeparator()); } - catch (IOException e) { + catch (Exception e) { + LOG.error(e); ApplicationManager.getApplication().invokeLater(() -> Messages.showErrorDialog(myView, e.getMessage())); } } + private static BufferedWriter getWriter(String outputDirectoryName, String name) throws FileNotFoundException, XMLStreamException { + File file = new File(outputDirectoryName, name + InspectionApplication.XML_EXTENSION); + FileUtil.createParentDirs(file); + return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), CharsetToolkit.UTF8_CHARSET)); + } + private static void writeDocument(@NotNull Element problems, String outputDirectoryName, String name) throws IOException { if (problems.getContentSize() != 0) { JDOMUtil.writeDocument(new Document(problems), diff --git a/platform/testFramework/src/com/intellij/testFramework/InspectionTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/InspectionTestUtil.java index 475eef947eb6..3e8c2e237e59 100644 --- a/platform/testFramework/src/com/intellij/testFramework/InspectionTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/InspectionTestUtil.java @@ -150,7 +150,7 @@ public class InspectionTestUtil { for (InspectionToolWrapper toolWrapper : toolWrappers) { InspectionToolPresentation presentation = context.getPresentation(toolWrapper); presentation.updateContent(); //e.g. dead code need check for reachables - presentation.exportResults(root, x -> false, x -> false); + presentation.exportResults(p -> root.addContent(p), x -> false, x -> false); } try {