SSR: fix compiled pattern caching (IDEA-114049)

This commit is contained in:
Bas Leijdekkers
2015-10-25 19:53:12 +01:00
parent 3445d6ad80
commit 2e5b567086
5 changed files with 67 additions and 116 deletions
@@ -12,9 +12,6 @@
<localInspection shortName="SSBasedInspection" bundle="messages.SSRBundle" key="SSRInspection.display.name" groupName="General"
enabledByDefault="false" level="WARNING"
implementationClass="com.intellij.structuralsearch.inspection.highlightTemplate.SSBasedInspection"/>
<postStartupActivity
implementation="com.intellij.structuralsearch.inspection.highlightTemplate.SSBasedInspectionCompiledPatternsCache"/>
<!--<fileBasedIndex implementation="com.intellij.tokenindex.TokenIndex"/>-->
<structuralsearch.profile implementation="com.intellij.structuralsearch.XmlStructuralSearchProfile"/>
<projectService serviceImplementation="com.intellij.structuralsearch.plugin.StructuralSearchPlugin"/>
<projectService serviceImplementation="com.intellij.structuralsearch.plugin.ui.ExistingTemplatesComponent"/>
@@ -38,9 +38,7 @@ import com.intellij.util.indexing.FileBasedIndex;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.*;
/**
* This class makes program structure tree matching:
@@ -193,10 +191,11 @@ public class MatcherImpl {
options.setScope(scope);
}
public CompiledOptions precompileOptions(List<Configuration> configurations) {
final List<Pair<MatchContext, Configuration>> contexts = new ArrayList<Pair<MatchContext, Configuration>>();
public void precompileOptions(List<Configuration> configurations, final Map<Configuration, MatchContext> out) {
for (final Configuration configuration : configurations) {
if (out.containsKey(configuration)) {
continue;
}
final MatchContext matchContext = new MatchContext();
matchContext.setMatcher(visitor);
final MatchOptions matchOptions = configuration.getMatchOptions();
@@ -206,16 +205,15 @@ public class MatcherImpl {
@Override
public void run() {
try {
CompiledPattern compiledPattern = PatternCompiler.compilePattern(project, matchOptions);
final CompiledPattern compiledPattern = PatternCompiler.compilePattern(project, matchOptions);
matchContext.setPattern(compiledPattern);
contexts.add(Pair.create(matchContext, configuration));
out.put(configuration, matchContext);
}
catch (UnsupportedPatternException ignored) {}
catch (MalformedPatternException ignored) {}
}
});
}
return new CompiledOptions(contexts);
}
Project getProject() {
@@ -23,9 +23,7 @@ import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.structuralsearch.MatchResult;
@@ -41,7 +39,6 @@ import com.intellij.structuralsearch.plugin.replace.impl.Replacer;
import com.intellij.structuralsearch.plugin.replace.ui.ReplaceConfiguration;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.structuralsearch.plugin.ui.ConfigurationManager;
import com.intellij.structuralsearch.plugin.ui.SearchContext;
import com.intellij.util.PairProcessor;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
@@ -59,7 +56,7 @@ public class SSBasedInspection extends LocalInspectionTool {
private static final Object LOCK = new Object(); // hack to avoid race conditions in SSR
static final String SHORT_NAME = "SSBasedInspection";
private List<Configuration> myConfigurations = new ArrayList<Configuration>();
private final List<Configuration> myConfigurations = new ArrayList<Configuration>();
private final Set<String> myProblemsReported = new HashSet<String>(1);
@Override
@@ -96,13 +93,12 @@ public class SSBasedInspection extends LocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
final MatcherImpl.CompiledOptions compiledOptions =
SSBasedInspectionCompiledPatternsCache.getCompiledOptions(holder.getProject());
final Map<Configuration, MatchContext> compiledOptions =
SSBasedInspectionCompiledPatternsCache.getCompiledOptions(myConfigurations, holder.getProject());
if (compiledOptions == null) return super.buildVisitor(holder, isOnTheFly);
if (compiledOptions.isEmpty()) return super.buildVisitor(holder, isOnTheFly);
return new PsiElementVisitor() {
final List<Pair<MatchContext,Configuration>> contexts = compiledOptions.getMatchContexts();
final Matcher matcher = new Matcher(holder.getManager().getProject());
final PairProcessor<MatchResult, Configuration> processor = new PairProcessor<MatchResult, Configuration>() {
@Override
@@ -122,9 +118,8 @@ public class SSBasedInspection extends LocalInspectionTool {
synchronized (LOCK) {
if (LexicalNodesFilter.getInstance().accepts(element)) return;
final SsrFilteringNodeIterator matchedNodes = new SsrFilteringNodeIterator(element);
for (Pair<MatchContext, Configuration> pair : contexts) {
Configuration configuration = pair.second;
MatchContext context = pair.first;
for (Configuration configuration : myConfigurations) {
final MatchContext context = compiledOptions.get(configuration);
if (MatcherImpl.checkIfShouldAttemptToMatch(context, matchedNodes)) {
final int nodeCount = context.getPattern().getNodeCount();
@@ -179,23 +174,12 @@ public class SSBasedInspection extends LocalInspectionTool {
@Override
@Nullable
public JComponent createOptionsPanel() {
return new SSBasedInspectionOptions(myConfigurations){
@Override
public void configurationsChanged(final SearchContext searchContext) {
super.configurationsChanged(searchContext);
SSBasedInspectionCompiledPatternsCache.precompileConfigurations(searchContext.getProject(), SSBasedInspection.this);
InspectionProfileManager.getInstance().fireProfileChanged(null);
}
}.getComponent();
return new SSBasedInspectionOptions(myConfigurations).getComponent();
}
@TestOnly
public void setConfigurations(final List<Configuration> configurations, final Project project) {
myConfigurations = configurations;
SSBasedInspectionCompiledPatternsCache.setCompiledOptions(project, configurations);
}
public List<Configuration> getConfigurations() {
return myConfigurations;
public void setConfigurations(@NotNull final List<Configuration> configurations, @NotNull final Project project) {
myConfigurations.clear();
myConfigurations.addAll(configurations);
}
}
@@ -1,84 +1,50 @@
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.util.Key;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.impl.matcher.MatcherImpl;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.plugin.ui.Configuration;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Eugene.Kudelevsky
*/
public class SSBasedInspectionCompiledPatternsCache implements StartupActivity {
private static final Key<MatcherImpl.CompiledOptions> COMPILED_OPTIONS_KEY = Key.create("SSR_INSPECTION_COMPILED_OPTIONS_KEY");
public class SSBasedInspectionCompiledPatternsCache {
@Override
public void runActivity(@NotNull final Project project) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
precompileConfigurations(project, null);
private static final Key<Map<Configuration, MatchContext>> COMPILED_OPTIONS_KEY = Key.create("SSR_INSPECTION_COMPILED_OPTIONS_KEY");
@NotNull
static Map<Configuration, MatchContext> getCompiledOptions(@NotNull List<Configuration> configurations, @NotNull Project project) {
final Map<Configuration, MatchContext> cache =
ObjectUtils.notNull(project.getUserData(COMPILED_OPTIONS_KEY), new HashMap<Configuration, MatchContext>());
if (!areConfigurationsInCache(configurations, cache)) {
final Matcher matcher = new Matcher(project);
matcher.precompileOptions(configurations, cache);
project.putUserData(COMPILED_OPTIONS_KEY, cache);
}
return cache;
}
private static boolean areConfigurationsInCache(@NotNull List<Configuration> configurations,
@NotNull Map<Configuration, MatchContext> cache) {
for (Configuration configuration : configurations) {
if (!(cache.containsKey(configuration))) {
return false;
}
});
}
return true;
}
static void precompileConfigurations(final Project project, @Nullable final SSBasedInspection ssBasedInspection) {
if (project.isDisposed()) {
return;
public static void removeFromCache(Configuration configuration, @NotNull Project project) {
final Map<Configuration, MatchContext> cache = project.getUserData(COMPILED_OPTIONS_KEY);
if (cache != null) {
cache.remove(configuration);
}
final MatcherImpl.CompiledOptions currentCompiledOptions = getCompiledOptions(project);
final SSBasedInspection inspection = ssBasedInspection != null ? ssBasedInspection : getInspection(project);
if (inspection == null) {
return;
}
List<Configuration> configurations = inspection.getConfigurations();
if (configurations == null) {
configurations = Collections.emptyList();
}
if ((currentCompiledOptions == null || currentCompiledOptions.getMatchContexts().isEmpty()) &&
configurations.isEmpty()) {
return;
}
final Matcher matcher = new Matcher(project);
final MatcherImpl.CompiledOptions compiledOptions = matcher.precompileOptions(configurations);
if (compiledOptions != null) {
project.putUserData(COMPILED_OPTIONS_KEY, compiledOptions);
}
}
@Nullable
private static SSBasedInspection getInspection(@NotNull Project project) {
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
final InspectionToolWrapper entry = profile.getInspectionTool(SSBasedInspection.SHORT_NAME, project);
return entry == null ? null : (SSBasedInspection)entry.getTool();
}
@Nullable
static MatcherImpl.CompiledOptions getCompiledOptions(@NotNull Project project) {
return project.getUserData(COMPILED_OPTIONS_KEY);
}
@TestOnly
static void setCompiledOptions(@NotNull Project project, @NotNull List<Configuration> configurations) {
final Matcher matcher = new Matcher(project);
project.putUserData(COMPILED_OPTIONS_KEY,
matcher.precompileOptions(configurations));
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.DumbService;
@@ -32,7 +33,6 @@ import org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.List;
/**
@@ -100,6 +100,7 @@ public class SSBasedInspectionOptions {
public void configurationsChanged(final SearchContext searchContext) {
((MyListModel)myTemplatesList.getModel()).fireContentsChanged();
DaemonCodeAnalyzer.getInstance(searchContext.getProject()).restart();
}
public JPanel getComponent() {
@@ -152,18 +153,14 @@ public class SSBasedInspectionOptions {
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
Object[] selected = myTemplatesList.getSelectedValues();
final SearchContext context = createSearchContext();
final Object[] selected = myTemplatesList.getSelectedValues();
for (Object o : selected) {
Configuration configuration = (Configuration)o;
Iterator<Configuration> iterator = myConfigurations.iterator();
while (iterator.hasNext()) {
Configuration configuration1 = iterator.next();
if (configuration1.getName().equals(configuration.getName())) {
iterator.remove();
}
}
final Configuration configuration = (Configuration)o;
myConfigurations.remove(configuration);
SSBasedInspectionCompiledPatternsCache.removeFromCache(configuration, context.getProject());
}
configurationsChanged(createSearchContext());
configurationsChanged(context);
}
}).setRemoveActionUpdater(new AnActionButtonUpdater() {
@Override
@@ -225,9 +222,11 @@ public class SSBasedInspectionOptions {
if (configuration == null) return;
SearchDialog dialog = createDialog(new SearchDialogFactory() {
@Override
public SearchDialog createDialog(SearchContext searchContext) {
if (configuration instanceof SearchConfiguration) {
return new SearchDialog(searchContext, false, false) {
@Override
public Configuration createConfiguration() {
SearchConfiguration newConfiguration = new SearchConfiguration();
copyConfiguration(configuration, newConfiguration);
@@ -237,6 +236,7 @@ public class SSBasedInspectionOptions {
}
else {
return new ReplaceDialog(searchContext, false, false) {
@Override
public Configuration createConfiguration() {
ReplaceConfiguration newConfiguration = new ReplaceConfiguration();
copyConfiguration(configuration, newConfiguration);
@@ -251,16 +251,22 @@ public class SSBasedInspectionOptions {
if (!dialog.showAndGet()) {
return;
}
Configuration newConfiguration = dialog.getConfiguration();
copyConfiguration(newConfiguration, configuration);
configurationsChanged(dialog.getSearchContext());
final Configuration newConfiguration = dialog.getConfiguration();
final int index = myConfigurations.indexOf(configuration);
myConfigurations.remove(index);
myConfigurations.add(index, newConfiguration);
final SearchContext context = dialog.getSearchContext();
SSBasedInspectionCompiledPatternsCache.removeFromCache(configuration, context.getProject());
configurationsChanged(context);
}
private class MyListModel extends AbstractListModel {
@Override
public int getSize() {
return myConfigurations.size();
}
@Override
public Object getElementAt(int index) {
return index < myConfigurations.size() ? myConfigurations.get(index) : null;
}