IDEA-132440 (quick fix corrected; cleanup)

This commit is contained in:
Roman Shevchenko
2014-11-06 17:11:57 +01:00
parent 7f5aff2c9b
commit 72093f557c
2 changed files with 75 additions and 117 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* 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.
@@ -18,17 +18,15 @@ package com.intellij.codeInspection.defaultFileTemplateUsage;
import com.intellij.codeInspection.*;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.impl.FileTemplateConfigurable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author cdr
*/
@@ -60,26 +58,6 @@ public class DefaultFileTemplateUsageInspection extends BaseJavaLocalInspectionT
return "DefaultFileTemplate";
}
static Pair<? extends PsiElement, ? extends PsiElement> getInteriorRange(PsiCodeBlock codeBlock) {
PsiElement[] children = codeBlock.getChildren();
if (children.length == 0) return Pair.create(codeBlock, codeBlock);
int start;
for (start=0; start<children.length;start++) {
PsiElement child = children[start];
if (child instanceof PsiWhiteSpace) continue;
if (child instanceof PsiJavaToken && ((PsiJavaToken)child).getTokenType() == JavaTokenType.LBRACE) continue;
break;
}
int end;
for (end=children.length-1; end > start;end--) {
PsiElement child = children[end];
if (child instanceof PsiWhiteSpace) continue;
if (child instanceof PsiJavaToken && ((PsiJavaToken)child).getTokenType() == JavaTokenType.RBRACE) continue;
break;
}
return Pair.create(children[start], children[end]);
}
@Override
@Nullable
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
@@ -92,15 +70,15 @@ public class DefaultFileTemplateUsageInspection extends BaseJavaLocalInspectionT
return true;
}
public static LocalQuickFix createEditFileTemplateFix(final FileTemplate templateToEdit, final ReplaceWithFileTemplateFix replaceTemplateFix) {
return new MyLocalQuickFix(templateToEdit, replaceTemplateFix);
public static LocalQuickFix createEditFileTemplateFix(FileTemplate templateToEdit, ReplaceWithFileTemplateFix replaceTemplateFix) {
return new EditFileTemplateFix(templateToEdit, replaceTemplateFix);
}
private static class MyLocalQuickFix implements LocalQuickFix {
private static class EditFileTemplateFix implements LocalQuickFix {
private final FileTemplate myTemplateToEdit;
private final ReplaceWithFileTemplateFix myReplaceTemplateFix;
public MyLocalQuickFix(FileTemplate templateToEdit, ReplaceWithFileTemplateFix replaceTemplateFix) {
public EditFileTemplateFix(FileTemplate templateToEdit, ReplaceWithFileTemplateFix replaceTemplateFix) {
myTemplateToEdit = templateToEdit;
myReplaceTemplateFix = replaceTemplateFix;
}
@@ -120,11 +98,10 @@ public class DefaultFileTemplateUsageInspection extends BaseJavaLocalInspectionT
@Override
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
final FileTemplateConfigurable configurable = new FileTemplateConfigurable();
SwingUtilities.invokeLater(new Runnable(){
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
configurable.setTemplate(myTemplateToEdit, null);
boolean ok = ShowSettingsUtil.getInstance().editConfigurable(project, configurable);
if (ok) {
WriteCommandAction.runWriteCommandAction(project, new Runnable() {
@@ -20,97 +20,97 @@ import com.intellij.codeInspection.*;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Alexey
* @author cdr
*/
public class FileHeaderChecker {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.defaultFileTemplateUsage.FileHeaderChecker");
static ProblemDescriptor checkFileHeader(@NotNull final PsiFile file, @NotNull InspectionManager manager, boolean onTheFly) {
static ProblemDescriptor checkFileHeader(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean onTheFly) {
TIntObjectHashMap<String> offsetToProperty = new TIntObjectHashMap<String>();
FileTemplate defaultTemplate = FileTemplateManager.getInstance().getDefaultTemplate(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME);
Pattern pattern = getTemplatePattern(defaultTemplate, file.getProject(), offsetToProperty);
Matcher matcher = pattern.matcher(file.getViewProvider().getContents());
if (matcher.matches()) {
final int startOffset = matcher.start(1);
final int endOffset = matcher.end(1);
final Ref<PsiDocComment> docComment = new Ref<PsiDocComment>();
file.accept(new JavaRecursiveElementWalkingVisitor(){
@Override public void visitElement(PsiElement element) {
if (docComment.get() != null) return;
TextRange range = element.getTextRange();
if (!range.contains(startOffset) && !range.contains(endOffset)) return;
super.visitElement(element);
}
@Override public void visitDocComment(PsiDocComment comment) {
docComment.set(comment);
}
});
PsiDocComment element = docComment.get();
if (element == null) return null;
LocalQuickFix[] quickFix = createQuickFix(matcher, offsetToProperty);
final String description = InspectionsBundle.message("default.file.template.description");
return manager.createProblemDescriptor(element, description, onTheFly, quickFix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
return null;
if (!matcher.matches()) return null;
final int startOffset = matcher.start(1);
final int endOffset = matcher.end(1);
final Ref<PsiDocComment> docComment = new Ref<PsiDocComment>();
file.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
if (docComment.get() != null) return;
TextRange range = element.getTextRange();
if (!range.contains(startOffset) && !range.contains(endOffset)) return;
super.visitElement(element);
}
@Override
public void visitDocComment(PsiDocComment comment) {
docComment.set(comment);
}
});
PsiDocComment element = docComment.get();
if (element == null) return null;
LocalQuickFix[] fixes = createQuickFix(matcher, offsetToProperty);
String description = InspectionsBundle.message("default.file.template.description");
return manager.createProblemDescriptor(element, description, onTheFly, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
}
public static Pattern getTemplatePattern(@NotNull FileTemplate template, @NotNull Project project, @NotNull TIntObjectHashMap<String> offsetToProperty) {
public static Pattern getTemplatePattern(@NotNull FileTemplate template,
@NotNull Project project,
@NotNull TIntObjectHashMap<String> offsetToProperty) {
String templateText = template.getText().trim();
String regex = templateToRegex(templateText, offsetToProperty, project);
regex = StringUtil.replace(regex, "with", "(?:with|by)");
regex = ".*("+regex+").*";
regex = ".*(" + regex + ").*";
return Pattern.compile(regex, Pattern.DOTALL);
}
private static Properties computeProperties(final Matcher matcher, final TIntObjectHashMap<String> offsetToProperty) {
Properties properties = new Properties(FileTemplateManager.getInstance().getDefaultProperties());
int[] offsets = offsetToProperty.keys();
Arrays.sort(offsets);
for (int i = 0; i < offsets.length; i++) {
final int offset = offsets[i];
String propName = offsetToProperty.get(offset);
int groupNum = i + 2; // first group is whole doc comment
String propValue = matcher.group(groupNum);
properties.put(propName, propValue);
properties.setProperty(propName, propValue);
}
return properties;
}
private static LocalQuickFix[] createQuickFix(final Matcher matcher,
final TIntObjectHashMap<String> offsetToProperty) {
private static LocalQuickFix[] createQuickFix(final Matcher matcher, final TIntObjectHashMap<String> offsetToProperty) {
final FileTemplate template = FileTemplateManager.getInstance().getPattern(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME);
final ReplaceWithFileTemplateFix replaceTemplateFix = new ReplaceWithFileTemplateFix() {
ReplaceWithFileTemplateFix replaceTemplateFix = new ReplaceWithFileTemplateFix() {
@Override
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
if (element == null || !element.isValid()) return;
if (!CodeInsightUtil.preparePsiElementsForWrite(element)) return;
String newText;
try {
newText = template.getText(computeProperties(matcher, offsetToProperty));
@@ -119,49 +119,30 @@ public class FileHeaderChecker {
LOG.error(e);
return;
}
try {
int offset = element.getTextRange().getStartOffset();
PsiFile psiFile = element.getContainingFile();
if (psiFile == null) return;
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(psiFile.getProject());
Document document = documentManager.getDocument(psiFile);
if (document == null) return;
element.delete();
documentManager.doPostponedOperationsAndUnblockDocument(document);
documentManager.commitDocument(document);
document.insertString(offset, newText);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
catch (IllegalStateException e) {
LOG.error("Cannot create doc comment from text: '" + newText + "'", e);
}
PsiDocComment newComment = JavaPsiFacade.getElementFactory(project).createDocCommentFromText(newText);
element.replace(newComment);
}
};
final LocalQuickFix editFileTemplateFix = DefaultFileTemplateUsageInspection.createEditFileTemplateFix(template, replaceTemplateFix);
if (template.isDefault()) {
return new LocalQuickFix[]{editFileTemplateFix};
}
return new LocalQuickFix[]{replaceTemplateFix,editFileTemplateFix};
LocalQuickFix editFileTemplateFix = DefaultFileTemplateUsageInspection.createEditFileTemplateFix(template, replaceTemplateFix);
return template.isDefault() ? new LocalQuickFix[]{editFileTemplateFix} : new LocalQuickFix[]{replaceTemplateFix, editFileTemplateFix};
}
private static String templateToRegex(@NotNull String text, @NotNull TIntObjectHashMap<String> offsetToProperty, @NotNull Project project) {
String regex = text;
@NonNls Collection<String> properties = new ArrayList<String>((Collection)FileTemplateManager.getInstance().getDefaultProperties(project).keySet());
private static String templateToRegex(String text, TIntObjectHashMap<String> offsetToProperty, Project project) {
List<Object> properties = ContainerUtil.newArrayList(FileTemplateManager.getInstance().getDefaultProperties(project).keySet());
properties.add("PACKAGE_NAME");
regex = escapeRegexChars(regex);
String regex = escapeRegexChars(text);
// first group is a whole file header
int groupNumber = 1;
for (String name : properties) {
String escaped = escapeRegexChars("${"+name+"}");
for (Object property : properties) {
String name = property.toString();
String escaped = escapeRegexChars("${" + name + "}");
boolean first = true;
for (int i = regex.indexOf(escaped); i!=-1 && i<regex.length(); i = regex.indexOf(escaped,i+1)) {
for (int i = regex.indexOf(escaped); i != -1 && i < regex.length(); i = regex.indexOf(escaped, i + 1)) {
String replacement = first ? "([^\\n]*)" : "\\" + groupNumber;
final int delta = escaped.length() - replacement.length();
int delta = escaped.length() - replacement.length();
int[] offs = offsetToProperty.keys();
for (int off : offs) {
if (off > i) {
@@ -170,7 +151,7 @@ public class FileHeaderChecker {
}
}
offsetToProperty.put(i, name);
regex = regex.substring(0,i) + replacement + regex.substring(i+escaped.length());
regex = regex.substring(0, i) + replacement + regex.substring(i + escaped.length());
if (first) {
groupNumber++;
first = false;
@@ -181,18 +162,18 @@ public class FileHeaderChecker {
}
private static String escapeRegexChars(String regex) {
regex = StringUtil.replace(regex,"|", "\\|");
regex = StringUtil.replace(regex,".", "\\.");
regex = StringUtil.replace(regex,"*", "\\*");
regex = StringUtil.replace(regex,"+", "\\+");
regex = StringUtil.replace(regex,"?", "\\?");
regex = StringUtil.replace(regex,"$", "\\$");
regex = StringUtil.replace(regex,"(", "\\(");
regex = StringUtil.replace(regex,")", "\\)");
regex = StringUtil.replace(regex,"[", "\\[");
regex = StringUtil.replace(regex,"]", "\\]");
regex = StringUtil.replace(regex,"{", "\\{");
regex = StringUtil.replace(regex,"}", "\\}");
regex = StringUtil.replace(regex, "|", "\\|");
regex = StringUtil.replace(regex, ".", "\\.");
regex = StringUtil.replace(regex, "*", "\\*");
regex = StringUtil.replace(regex, "+", "\\+");
regex = StringUtil.replace(regex, "?", "\\?");
regex = StringUtil.replace(regex, "$", "\\$");
regex = StringUtil.replace(regex, "(", "\\(");
regex = StringUtil.replace(regex, ")", "\\)");
regex = StringUtil.replace(regex, "[", "\\[");
regex = StringUtil.replace(regex, "]", "\\]");
regex = StringUtil.replace(regex, "{", "\\{");
regex = StringUtil.replace(regex, "}", "\\}");
return regex;
}
}