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
.codeInsight
.daemon
.impl
.analysis
;
18 import com
.intellij
.codeHighlighting
.HighlightDisplayLevel
;
19 import com
.intellij
.codeInsight
.daemon
.XmlErrorMessages
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfoType
;
21 import com
.intellij
.codeInspection
.ProblemHighlightType
;
22 import com
.intellij
.codeInspection
.ProblemsHolder
;
23 import com
.intellij
.codeInspection
.XmlSuppressableInspectionTool
;
24 import com
.intellij
.lang
.injection
.InjectedLanguageManager
;
25 import com
.intellij
.openapi
.util
.TextRange
;
26 import com
.intellij
.psi
.*;
27 import com
.intellij
.psi
.templateLanguages
.OuterLanguageElement
;
28 import com
.intellij
.psi
.xml
.*;
29 import com
.intellij
.xml
.XmlBundle
;
30 import com
.intellij
.xml
.XmlElementDescriptor
;
31 import com
.intellij
.xml
.XmlExtension
;
32 import com
.intellij
.xml
.impl
.schema
.AnyXmlElementDescriptor
;
33 import com
.intellij
.xml
.util
.XmlTagUtil
;
34 import com
.intellij
.xml
.util
.XmlUtil
;
35 import org
.jetbrains
.annotations
.NonNls
;
36 import org
.jetbrains
.annotations
.NotNull
;
39 * @author Dmitry Avdeev
41 public class XmlUnboundNsPrefixInspection
extends XmlSuppressableInspectionTool
{
43 @NonNls private static final String XML
= "xml";
47 public PsiElementVisitor
buildVisitor(@NotNull final ProblemsHolder holder
, final boolean isOnTheFly
) {
48 return new XmlElementVisitor() {
50 private Boolean isXml
;
52 private boolean isXmlFile(XmlElement element
) {
54 final PsiFile file
= element
.getContainingFile();
55 isXml
= file
instanceof XmlFile
&& !InjectedLanguageManager
.getInstance(element
.getProject()).isInjectedFragment(file
);
57 return isXml
.booleanValue();
61 public void visitXmlToken(final XmlToken token
) {
62 if (isXmlFile(token
) && token
.getTokenType() == XmlTokenType
.XML_NAME
) {
63 PsiElement element
= token
.getPrevSibling();
64 while(element
instanceof PsiWhiteSpace
) element
= element
.getPrevSibling();
66 if (element
instanceof XmlToken
&& ((XmlToken
)element
).getTokenType() == XmlTokenType
.XML_START_TAG_START
) {
67 PsiElement parent
= element
.getParent();
69 if (parent
instanceof XmlTag
&& !(token
.getNextSibling() instanceof OuterLanguageElement
)) {
70 XmlTag tag
= (XmlTag
)parent
;
71 checkUnboundNamespacePrefix(tag
, tag
, tag
.getNamespacePrefix(), token
, holder
);
78 public void visitXmlAttribute(final XmlAttribute attribute
) {
79 if (!isXmlFile(attribute
)) {
82 final String namespace
= attribute
.getNamespace();
83 if (attribute
.isNamespaceDeclaration() || XmlUtil
.XML_SCHEMA_INSTANCE_URI
.equals(namespace
)) {
87 XmlTag tag
= attribute
.getParent();
88 XmlElementDescriptor elementDescriptor
= tag
.getDescriptor();
89 if (elementDescriptor
== null ||
90 elementDescriptor
instanceof AnyXmlElementDescriptor
) {
95 final String name
= attribute
.getName();
97 checkUnboundNamespacePrefix(attribute
, tag
, XmlUtil
.findPrefixByQualifiedName(name
), null, holder
);
102 private static void checkUnboundNamespacePrefix(final XmlElement element
, final XmlTag context
, String namespacePrefix
, final XmlToken token
,
103 final ProblemsHolder holder
) {
105 if (namespacePrefix
.length() == 0 && (!(element
instanceof XmlTag
) || !(element
.getParent() instanceof XmlDocument
))
106 || XML
.equals(namespacePrefix
)) {
110 final String namespaceByPrefix
= context
.getNamespaceByPrefix(namespacePrefix
);
111 if (namespaceByPrefix
.length() != 0) {
115 final XmlFile containingFile
= (XmlFile
)context
.getContainingFile();
116 if (!HighlightLevelUtil
.shouldInspect(containingFile
)) return;
118 final XmlExtension extension
= XmlExtension
.getExtension(containingFile
);
119 if (extension
.isPrefixDeclared(context
, namespacePrefix
)) {
123 final String localizedMessage
= XmlErrorMessages
.message("unbound.namespace", namespacePrefix
);
125 if (namespacePrefix
.length() == 0) {
126 final XmlTag tag
= (XmlTag
)element
;
127 if (!XmlUtil
.JSP_URI
.equals(tag
.getNamespace())) {
128 reportTagProblem(tag
, localizedMessage
, null, ProblemHighlightType
.INFORMATION
, new CreateNSDeclarationIntentionFix(context
, namespacePrefix
, token
),
134 final int prefixLength
= namespacePrefix
.length();
135 final TextRange range
= new TextRange(0, prefixLength
);
136 final HighlightInfoType infoType
= extension
.getHighlightInfoType(containingFile
);
137 final ProblemHighlightType highlightType
= infoType
== HighlightInfoType
.ERROR ? ProblemHighlightType
.ERROR
: ProblemHighlightType
.LIKE_UNKNOWN_SYMBOL
;
138 if (element
instanceof XmlTag
) {
139 final CreateNSDeclarationIntentionFix fix
= new CreateNSDeclarationIntentionFix(context
, namespacePrefix
, token
);
140 reportTagProblem(element
, localizedMessage
, range
, highlightType
, fix
, holder
);
142 holder
.registerProblem(element
, localizedMessage
, highlightType
, range
);
146 private static void reportTagProblem(final XmlElement element
, final String localizedMessage
, final TextRange range
, final ProblemHighlightType highlightType
,
147 final CreateNSDeclarationIntentionFix fix
,
148 final ProblemsHolder holder
) {
150 XmlToken nameToken
= XmlTagUtil
.getStartTagNameElement((XmlTag
)element
);
151 if (nameToken
!= null) {
152 holder
.registerProblem(nameToken
, localizedMessage
, highlightType
, range
, fix
);
154 nameToken
= XmlTagUtil
.getEndTagNameElement((XmlTag
)element
);
155 if (nameToken
!= null) {
156 holder
.registerProblem(nameToken
, localizedMessage
, highlightType
, range
, fix
);
162 public HighlightDisplayLevel
getDefaultLevel() {
163 return HighlightDisplayLevel
.WARNING
;
166 public boolean isEnabledByDefault() {
171 public String
getGroupDisplayName() {
172 return XmlBundle
.message("xml.inspections.group.name");
176 public String
getDisplayName() {
177 return XmlBundle
.message("xml.inspections.unbound.prefix");
182 public String
getShortName() {
183 return "XmlUnboundNsPrefix";