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
;
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
);
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
);
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;
95 newText
= template
.getText(computeProperties(matcher
, offsetToProperty
));
97 catch (IOException e
) {
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;
110 documentManager
.doPostponedOperationsAndUnblockDocument(document
);
111 documentManager
.commitDocument(document
);
113 document
.insertString(offset
, newText
);
115 catch (IncorrectOperationException 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
);
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
) {
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
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
) {
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());
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
,"}", "\\}");