Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / defaultFileTemplateUsage / FileHeaderChecker.java
blob7c468e3c18fcfa740da7d71cd63d11c72a400559
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInspection.defaultFileTemplateUsage;
18 import com.intellij.codeInsight.CodeInsightUtil;
19 import com.intellij.codeInspection.*;
20 import com.intellij.ide.fileTemplates.FileTemplate;
21 import com.intellij.ide.fileTemplates.FileTemplateManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.editor.Document;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Ref;
26 import com.intellij.openapi.util.TextRange;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.psi.JavaRecursiveElementWalkingVisitor;
29 import com.intellij.psi.PsiDocumentManager;
30 import com.intellij.psi.PsiElement;
31 import com.intellij.psi.PsiFile;
32 import com.intellij.psi.javadoc.PsiDocComment;
33 import com.intellij.util.IncorrectOperationException;
34 import gnu.trove.TIntObjectHashMap;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Properties;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
46 /**
47 * @author Alexey
49 public class FileHeaderChecker {
50 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.defaultFileTemplateUsage.FileHeaderChecker");
52 static ProblemDescriptor checkFileHeader(final PsiFile file, final InspectionManager manager, boolean onTheFly) {
53 FileTemplate template = FileTemplateManager.getInstance().getDefaultTemplate(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME);
54 TIntObjectHashMap<String> offsetToProperty = new TIntObjectHashMap<String>();
55 String templateText = template.getText().trim();
56 String regex = templateToRegex(templateText, offsetToProperty);
57 regex = ".*("+regex+").*";
58 String fileText = file.getText();
59 Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
60 Matcher matcher = pattern.matcher(fileText);
61 if (matcher.matches()) {
62 final int startOffset = matcher.start(1);
63 final int endOffset = matcher.end(1);
64 final Ref<PsiDocComment> docComment = new Ref<PsiDocComment>();
65 file.accept(new JavaRecursiveElementWalkingVisitor(){
66 @Override public void visitElement(PsiElement element) {
67 if (docComment.get() != null) return;
68 TextRange range = element.getTextRange();
69 if (!range.contains(startOffset) && !range.contains(endOffset)) return;
70 super.visitElement(element);
72 @Override public void visitDocComment(PsiDocComment comment) {
73 docComment.set(comment);
75 });
76 PsiDocComment element = docComment.get();
77 if (element == null) return null;
78 LocalQuickFix[] quickFix = createQuickFix(element, matcher, offsetToProperty);
79 final String description = InspectionsBundle.message("default.file.template.description");
80 return manager.createProblemDescriptor(element, description, quickFix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly);
82 return null;
85 private static LocalQuickFix[] createQuickFix(final PsiDocComment element,
86 final Matcher matcher,
87 final TIntObjectHashMap<String> offsetToProperty) {
88 final FileTemplate template = FileTemplateManager.getInstance().getPattern(FileTemplateManager.FILE_HEADER_TEMPLATE_NAME);
89 final ReplaceWithFileTemplateFix replaceTemplateFix = new ReplaceWithFileTemplateFix() {
90 public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
91 if (!element.isValid()) return;
92 if (!CodeInsightUtil.preparePsiElementsForWrite(element)) return;
93 String newText;
94 try {
95 newText = template.getText(computeProperties(matcher, offsetToProperty));
97 catch (IOException e) {
98 LOG.error(e);
99 return;
101 try {
102 int offset = element.getTextRange().getStartOffset();
103 PsiFile psiFile = element.getContainingFile();
104 if (psiFile == null) return;
105 PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
106 Document document = documentManager.getDocument(psiFile);
107 if (document == null) return;
109 element.delete();
110 documentManager.doPostponedOperationsAndUnblockDocument(document);
111 documentManager.commitDocument(document);
113 document.insertString(offset, newText);
115 catch (IncorrectOperationException e) {
116 LOG.error(e);
118 catch (IllegalStateException e) {
119 LOG.error("Cannot create doc comment from text: '"+newText+"'",e);
123 private Properties computeProperties(final Matcher matcher, final TIntObjectHashMap<String> offsetToProperty) {
124 Properties properties = new Properties(FileTemplateManager.getInstance().getDefaultProperties());
125 int[] offsets = offsetToProperty.keys();
126 Arrays.sort(offsets);
128 for (int i = 0; i < offsets.length; i++) {
129 final int offset = offsets[i];
130 String propName = offsetToProperty.get(offset);
131 int groupNum = i + 2; // first group is whole doc comment
132 String propValue = matcher.group(groupNum);
133 properties.put(propName, propValue);
136 return properties;
139 final LocalQuickFix editFileTemplateFix = DefaultFileTemplateUsageInspection.createEditFileTemplateFix(template, replaceTemplateFix);
140 if (template.isDefault()) {
141 return new LocalQuickFix[]{editFileTemplateFix};
143 return new LocalQuickFix[]{replaceTemplateFix,editFileTemplateFix};
146 private static String templateToRegex(final String text, TIntObjectHashMap<String> offsetToProperty) {
147 String regex = text;
148 @NonNls Collection<String> properties = new ArrayList<String>((Collection)FileTemplateManager.getInstance().getDefaultProperties().keySet());
149 properties.add("PACKAGE_NAME");
151 regex = escapeRegexChars(regex);
152 // first group is a whole file header
153 int groupNumber = 1;
154 for (String name : properties) {
155 String escaped = escapeRegexChars("${"+name+"}");
156 boolean first = true;
157 for (int i = regex.indexOf(escaped); i!=-1 && i<regex.length(); i = regex.indexOf(escaped,i+1)) {
158 String replacement = first ? "(.*)" : "\\" + groupNumber;
159 final int delta = escaped.length() - replacement.length();
160 int[] offs = offsetToProperty.keys();
161 for (int off : offs) {
162 if (off > i) {
163 String prop = offsetToProperty.remove(off);
164 offsetToProperty.put(off - delta, prop);
167 offsetToProperty.put(i, name);
168 regex = regex.substring(0,i) + replacement + regex.substring(i+escaped.length());
169 if (first) {
170 groupNumber++;
171 first = false;
175 return regex;
178 private static String escapeRegexChars(String regex) {
179 regex = StringUtil.replace(regex,"|", "\\|");
180 regex = StringUtil.replace(regex,".", "\\.");
181 regex = StringUtil.replace(regex,"*", "\\*");
182 regex = StringUtil.replace(regex,"+", "\\+");
183 regex = StringUtil.replace(regex,"?", "\\?");
184 regex = StringUtil.replace(regex,"$", "\\$");
185 regex = StringUtil.replace(regex,"(", "\\(");
186 regex = StringUtil.replace(regex,")", "\\)");
187 regex = StringUtil.replace(regex,"[", "\\[");
188 regex = StringUtil.replace(regex,"]", "\\]");
189 regex = StringUtil.replace(regex,"{", "\\{");
190 regex = StringUtil.replace(regex,"}", "\\}");
191 return regex;