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.
18 * Created by IntelliJ IDEA.
19 * User: Maxim.Mossienko
23 package com
.intellij
.xml
.util
;
25 import com
.intellij
.codeHighlighting
.HighlightDisplayLevel
;
26 import com
.intellij
.codeInsight
.CodeInsightUtilBase
;
27 import com
.intellij
.codeInspection
.*;
28 import com
.intellij
.lexer
.Lexer
;
29 import com
.intellij
.lexer
.XmlLexer
;
30 import com
.intellij
.openapi
.editor
.Editor
;
31 import com
.intellij
.openapi
.fileEditor
.FileEditorManager
;
32 import com
.intellij
.openapi
.fileEditor
.OpenFileDescriptor
;
33 import com
.intellij
.openapi
.fileTypes
.FileType
;
34 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
35 import com
.intellij
.openapi
.project
.Project
;
36 import com
.intellij
.openapi
.util
.TextRange
;
37 import com
.intellij
.psi
.PsiElement
;
38 import com
.intellij
.psi
.PsiElementVisitor
;
39 import com
.intellij
.psi
.PsiFile
;
40 import com
.intellij
.psi
.XmlElementVisitor
;
41 import com
.intellij
.psi
.html
.HtmlTag
;
42 import com
.intellij
.psi
.impl
.source
.tree
.TreeUtil
;
43 import com
.intellij
.psi
.tree
.IElementType
;
44 import com
.intellij
.psi
.xml
.XmlTag
;
45 import com
.intellij
.psi
.xml
.XmlTagValue
;
46 import com
.intellij
.psi
.xml
.XmlTokenType
;
47 import com
.intellij
.xml
.XmlBundle
;
48 import org
.jetbrains
.annotations
.NonNls
;
49 import org
.jetbrains
.annotations
.NotNull
;
52 * @author Maxim Mossienko
54 public class CheckValidXmlInScriptBodyInspection
extends XmlSuppressableInspectionTool
{
56 private static final String SCRIPT_TAG_NAME
= "script";
57 private Lexer myXmlLexer
;
59 private static final String AMP_ENTITY_REFERENCE
= "&";
61 private static final String LT_ENTITY_REFERENCE
= "<";
63 public boolean isEnabledByDefault() {
68 public PsiElementVisitor
buildVisitor(@NotNull final ProblemsHolder holder
, boolean isOnTheFly
) {
69 return new XmlElementVisitor() {
70 @Override public void visitXmlTag(final XmlTag tag
) {
71 if (SCRIPT_TAG_NAME
.equals(tag
.getName()) ||
72 (tag
instanceof HtmlTag
&& SCRIPT_TAG_NAME
.equalsIgnoreCase(tag
.getName()))
74 final PsiFile psiFile
= tag
.getContainingFile();
75 final FileType fileType
= psiFile
.getFileType();
77 if (fileType
== StdFileTypes
.XHTML
|| fileType
== StdFileTypes
.JSPX
) {
78 synchronized(CheckValidXmlInScriptBodyInspection
.class) {
79 if (myXmlLexer
== null) myXmlLexer
= new XmlLexer();
80 final XmlTagValue tagValue
= tag
.getValue();
81 final String tagBodyText
= tagValue
.getText();
83 if (tagBodyText
.length() > 0) {
84 myXmlLexer
.start(tagBodyText
);
86 while(myXmlLexer
.getTokenType() != null) {
87 IElementType tokenType
= myXmlLexer
.getTokenType();
89 if (tokenType
== XmlTokenType
.XML_CDATA_START
) {
90 while(tokenType
!= null && tokenType
!= XmlTokenType
.XML_CDATA_END
) {
92 tokenType
= myXmlLexer
.getTokenType();
94 if (tokenType
== null) break;
96 if (( tokenType
== XmlTokenType
.XML_BAD_CHARACTER
&&
97 "&".equals(TreeUtil
.getTokenText(myXmlLexer
))
99 tokenType
== XmlTokenType
.XML_START_TAG_START
101 final int valueStart
= tagValue
.getTextRange().getStartOffset();
102 final int offset
= valueStart
+ myXmlLexer
.getTokenStart();
103 final PsiElement psiElement
= psiFile
.findElementAt(offset
);
104 final TextRange elementRange
= psiElement
.getTextRange();
106 final int offsetInElement
= offset
- elementRange
.getStartOffset();
107 holder
.registerProblem(
109 XmlBundle
.message("unescaped.xml.character"),
110 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
,
111 new InsertQuotedCharacterQuickFix(
118 int endOfElementInScriptTag
= elementRange
.getEndOffset() - valueStart
;
119 while(myXmlLexer
.getTokenEnd() < endOfElementInScriptTag
) {
120 myXmlLexer
.advance();
121 if (myXmlLexer
.getTokenType() == null) break;
124 myXmlLexer
.advance();
135 public String
getGroupDisplayName() {
136 return XmlInspectionGroupNames
.HTML_INSPECTIONS
;
140 public String
getDisplayName() {
141 return XmlBundle
.message("html.inspections.check.valid.script.tag");
146 public String
getShortName() {
147 return "CheckValidXmlInScriptTagBody";
150 private static class InsertQuotedCharacterQuickFix
implements LocalQuickFix
{
151 private final PsiFile psiFile
;
152 private final PsiElement psiElement
;
153 private final int startInElement
;
155 public InsertQuotedCharacterQuickFix(PsiFile psiFile
, PsiElement psiElement
, int startInElement
) {
156 this.psiFile
= psiFile
;
157 this.psiElement
= psiElement
;
158 this.startInElement
= startInElement
;
162 public String
getName() {
163 final String character
= getXmlCharacter();
165 return XmlBundle
.message(
166 "unescaped.xml.character.fix.message",
167 character
.equals("&") ?
168 XmlBundle
.message("unescaped.xml.character.fix.message.parameter"):
174 public String
getFamilyName() {
178 public void applyFix(@NotNull Project project
, @NotNull ProblemDescriptor problemDescriptor
) {
179 if ( !CodeInsightUtilBase
.prepareFileForWrite(psiFile
)) return;
180 final TextRange range
= psiElement
.getTextRange();
181 OpenFileDescriptor descriptor
= new OpenFileDescriptor(
183 psiFile
.getVirtualFile(),
184 range
.getStartOffset() + startInElement
187 final Editor editor
= FileEditorManager
.getInstance(project
).openTextEditor(descriptor
, true);
188 if (editor
== null) return;
190 final String xmlCharacter
= getXmlCharacter();
191 String replacement
= xmlCharacter
.equals("&") ? AMP_ENTITY_REFERENCE
: LT_ENTITY_REFERENCE
;
192 replacement
= psiElement
.getText().replace(xmlCharacter
,replacement
);
194 editor
.getDocument().replaceString(
195 range
.getStartOffset(),
196 range
.getEndOffset(),
201 private String
getXmlCharacter() {
202 return psiElement
.getText().substring(startInElement
, startInElement
+ 1);
207 public HighlightDisplayLevel
getDefaultLevel() {
208 return HighlightDisplayLevel
.ERROR
;