streaming write of inspection results (from toolwindow only)

This commit is contained in:
Dmitry Batkovich
2018-10-04 19:56:22 +03:00
parent d4456d65ef
commit b355f1a11b
8 changed files with 112 additions and 34 deletions
@@ -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<Element> problemSink,
@NotNull RefEntity refEntity,
@NotNull Predicate<? super CommonProblemDescriptor> 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
@@ -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);
@@ -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;
}
@@ -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);
@@ -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<Element> problemSink,
@NotNull final Predicate<? super RefEntity> excludedEntities,
@NotNull final Predicate<? super CommonProblemDescriptor> 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<Element> 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<Element> problemSink,
@NotNull RefEntity refEntity,
@NotNull Predicate<? super CommonProblemDescriptor> 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<Element> problemSink,
@NotNull Predicate<? super CommonProblemDescriptor> 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);
}
}
@@ -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<? super CommonProblemDescriptor> 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<Element> problemSink,
@NotNull RefEntity refEntity,
@NotNull Predicate<? super CommonProblemDescriptor> isDescriptorExcluded);
void exportResults(@NotNull Consumer<Element> problemSink,
@NotNull Predicate<? super RefEntity> isEntityExcluded,
@NotNull Predicate<? super CommonProblemDescriptor> isProblemExcluded);
@@ -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<InspectionToolWrapper> 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<InspectionToolWrapper> 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<InspectionToolWrapper> 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),
@@ -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 {