blob: 7a38ddb2ea65fdec792287c169953ca3ed770da4 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.structuralsearch.inspection.highlightTemplate;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.*;
import com.intellij.dupLocator.iterators.CountingNodeIterator;
import com.intellij.notification.Notification;
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;
import com.intellij.structuralsearch.Matcher;
import com.intellij.structuralsearch.SSRBundle;
import com.intellij.structuralsearch.StructuralSearchException;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.MatcherImpl;
import com.intellij.structuralsearch.impl.matcher.filters.LexicalNodesFilter;
import com.intellij.structuralsearch.impl.matcher.iterators.SsrFilteringNodeIterator;
import com.intellij.structuralsearch.plugin.replace.ReplacementInfo;
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;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.*;
/**
* @author cdr
*/
public class SSBasedInspection extends LocalInspectionTool {
static final String SHORT_NAME = "SSBasedInspection";
private List<Configuration> myConfigurations = new ArrayList<Configuration>();
private Set<String> myProblemsReported = new HashSet<String>(1);
public void writeSettings(@NotNull Element node) throws WriteExternalException {
ConfigurationManager.writeConfigurations(node, myConfigurations, Collections.<Configuration>emptyList());
}
public void readSettings(@NotNull Element node) throws InvalidDataException {
myProblemsReported.clear();
myConfigurations.clear();
ConfigurationManager.readConfigurations(node, myConfigurations, new ArrayList<Configuration>());
}
@NotNull
public String getGroupDisplayName() {
return GENERAL_GROUP_NAME;
}
@NotNull
public String getDisplayName() {
return SSRBundle.message("SSRInspection.display.name");
}
@NotNull
@NonNls
public String getShortName() {
return SHORT_NAME;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
final MatcherImpl.CompiledOptions compiledOptions =
SSBasedInspectionCompiledPatternsCache.getCompiledOptions(holder.getProject());
if (compiledOptions == null) 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>() {
public boolean process(MatchResult matchResult, Configuration configuration) {
PsiElement element = matchResult.getMatch();
String name = configuration.getName();
LocalQuickFix fix = createQuickFix(holder.getManager().getProject(), matchResult, configuration);
holder.registerProblem(
holder.getManager().createProblemDescriptor(element, name, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)
);
return true;
}
};
@Override
public void visitElement(PsiElement element) {
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;
if (MatcherImpl.checkIfShouldAttemptToMatch(context, matchedNodes)) {
final int nodeCount = context.getPattern().getNodeCount();
try {
matcher.processMatchesInElement(context, configuration, new CountingNodeIterator(nodeCount, matchedNodes), processor);
}
catch (StructuralSearchException e) {
if (myProblemsReported.add(configuration.getName())) { // don't overwhelm the user with messages
Notifications.Bus.notify(new Notification(SSRBundle.message("structural.search.title"),
SSRBundle.message("template.problem", configuration.getName()),
e.getMessage(),
NotificationType.ERROR), element.getProject());
}
}
matchedNodes.reset();
}
}
}
};
}
private static LocalQuickFix createQuickFix(final Project project, final MatchResult matchResult, final Configuration configuration) {
if (!(configuration instanceof ReplaceConfiguration)) return null;
ReplaceConfiguration replaceConfiguration = (ReplaceConfiguration)configuration;
final Replacer replacer = new Replacer(project, replaceConfiguration.getOptions());
final ReplacementInfo replacementInfo = replacer.buildReplacement(matchResult);
return new LocalQuickFix() {
@NotNull
public String getName() {
return SSRBundle.message("SSRInspection.replace.with", replacementInfo.getReplacement());
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element != null && FileModificationService.getInstance().preparePsiElementsForWrite(element)) {
replacer.replace(replacementInfo);
}
}
@NotNull
public String getFamilyName() {
return SSRBundle.message("SSRInspection.family.name");
}
};
}
@Nullable
public JComponent createOptionsPanel() {
return new SSBasedInspectionOptions(myConfigurations){
public void configurationsChanged(final SearchContext searchContext) {
super.configurationsChanged(searchContext);
SSBasedInspectionCompiledPatternsCache.precompileConfigurations(searchContext.getProject(), SSBasedInspection.this);
InspectionProfileManager.getInstance().fireProfileChanged(null);
}
}.getComponent();
}
@TestOnly
public void setConfigurations(final List<Configuration> configurations, final Project project) {
myConfigurations = configurations;
SSBasedInspectionCompiledPatternsCache.setCompiledOptions(project, configurations);
}
public List<Configuration> getConfigurations() {
return myConfigurations;
}
}