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
.codeInsight
.daemon
.*;
19 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfo
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightInfoType
;
21 import com
.intellij
.codeInsight
.daemon
.impl
.HighlightVisitor
;
22 import com
.intellij
.codeInsight
.daemon
.impl
.SeverityRegistrar
;
23 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.QuickFixAction
;
24 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.QuickFixActionRegistrarImpl
;
25 import com
.intellij
.codeInsight
.intention
.IntentionAction
;
26 import com
.intellij
.codeInsight
.quickfix
.UnresolvedReferenceQuickFixProvider
;
27 import com
.intellij
.codeInspection
.InspectionProfile
;
28 import com
.intellij
.codeInspection
.ex
.LocalInspectionToolWrapper
;
29 import com
.intellij
.codeInspection
.htmlInspections
.RequiredAttributesInspection
;
30 import com
.intellij
.codeInspection
.htmlInspections
.XmlEntitiesInspection
;
31 import com
.intellij
.idea
.LoggerFactory
;
32 import com
.intellij
.lang
.ASTNode
;
33 import com
.intellij
.lang
.dtd
.DTDLanguage
;
34 import com
.intellij
.openapi
.diagnostic
.Logger
;
35 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
36 import com
.intellij
.openapi
.progress
.ProgressManager
;
37 import com
.intellij
.openapi
.util
.Comparing
;
38 import com
.intellij
.openapi
.util
.Key
;
39 import com
.intellij
.openapi
.util
.TextRange
;
40 import com
.intellij
.openapi
.util
.text
.StringUtil
;
41 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
42 import com
.intellij
.psi
.*;
43 import com
.intellij
.psi
.html
.HtmlTag
;
44 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
45 import com
.intellij
.psi
.meta
.PsiMetaData
;
46 import com
.intellij
.psi
.templateLanguages
.OuterLanguageElement
;
47 import com
.intellij
.psi
.util
.PsiTreeUtil
;
48 import com
.intellij
.psi
.util
.PsiUtilBase
;
49 import com
.intellij
.psi
.xml
.*;
50 import com
.intellij
.util
.SmartList
;
51 import com
.intellij
.xml
.XmlAttributeDescriptor
;
52 import com
.intellij
.xml
.XmlElementDescriptor
;
53 import com
.intellij
.xml
.XmlExtension
;
54 import com
.intellij
.xml
.impl
.schema
.AnyXmlElementDescriptor
;
55 import com
.intellij
.xml
.util
.HtmlUtil
;
56 import com
.intellij
.xml
.util
.XmlTagUtil
;
57 import com
.intellij
.xml
.util
.XmlUtil
;
58 import org
.jetbrains
.annotations
.NotNull
;
59 import org
.jetbrains
.annotations
.Nullable
;
61 import java
.text
.MessageFormat
;
62 import java
.util
.HashSet
;
63 import java
.util
.List
;
65 import java
.util
.StringTokenizer
;
70 public class XmlHighlightVisitor
extends XmlElementVisitor
implements HighlightVisitor
, Validator
.ValidationHost
{
71 private static final Logger LOG
= LoggerFactory
.getInstance().getLoggerInstance("com.intellij.codeInsight.daemon.impl.analysis.XmlHighlightVisitor");
72 public static final Key
<String
> DO_NOT_VALIDATE_KEY
= Key
.create("do not validate");
73 private List
<HighlightInfo
> myResult
;
75 private static boolean ourDoJaxpTesting
;
77 private static final TextAttributes NONEMPTY_TEXT_ATTRIBUTES
= new TextAttributes() {
78 public boolean isEmpty() {
83 private void addElementsForTag(XmlTag tag
,
84 String localizedMessage
,
85 HighlightInfoType type
,
86 IntentionAction quickFixAction
) {
87 addElementsForTagWithManyQuickFixes(tag
, localizedMessage
, type
, quickFixAction
);
90 private void addElementsForTagWithManyQuickFixes(XmlTag tag
,
91 String localizedMessage
,
92 HighlightInfoType type
, IntentionAction
... quickFixActions
) {
93 bindMessageToTag(tag
, type
, -1, localizedMessage
, quickFixActions
);
96 @Override public void visitXmlToken(XmlToken token
) {
97 if (token
.getTokenType() == XmlTokenType
.XML_NAME
|| token
.getTokenType() == XmlTokenType
.XML_TAG_NAME
) {
98 PsiElement element
= token
.getPrevSibling();
99 while(element
instanceof PsiWhiteSpace
) element
= element
.getPrevSibling();
101 if (element
instanceof XmlToken
) {
102 if (((XmlToken
)element
).getTokenType() == XmlTokenType
.XML_START_TAG_START
) {
103 PsiElement parent
= element
.getParent();
105 if (parent
instanceof XmlTag
&& !(token
.getNextSibling() instanceof OuterLanguageElement
)) {
106 checkTag((XmlTag
)parent
);
110 PsiElement parent
= token
.getParent();
112 if (parent
instanceof XmlAttribute
&& !(token
.getNextSibling() instanceof OuterLanguageElement
)) {
113 checkAttribute((XmlAttribute
) parent
);
119 private void checkTag(XmlTag tag
) {
120 if (ourDoJaxpTesting
) return;
122 if (myResult
== null) {
123 checkTagByDescriptor(tag
);
126 if (myResult
== null) {
127 if (tag
.getUserData(DO_NOT_VALIDATE_KEY
) == null) {
128 final XmlElementDescriptor descriptor
= tag
.getDescriptor();
130 if (tag
instanceof HtmlTag
&&
131 ( descriptor
instanceof AnyXmlElementDescriptor
||
138 checkReferences(tag
);
143 private void bindMessageToTag(final XmlTag tag
, final HighlightInfoType warning
, final int messageLength
, final String localizedMessage
, IntentionAction
... quickFixActions
) {
144 XmlToken childByRole
= XmlTagUtil
.getStartTagNameElement(tag
);
146 bindMessageToAstNode(childByRole
, warning
, 0, messageLength
, localizedMessage
, quickFixActions
);
147 childByRole
= XmlTagUtil
.getEndTagNameElement(tag
);
148 bindMessageToAstNode(childByRole
, warning
, 0, messageLength
, localizedMessage
, quickFixActions
);
151 private void bindMessageToAstNode(final PsiElement childByRole
,
152 final HighlightInfoType warning
,
155 final String localizedMessage
, IntentionAction
... quickFixActions
) {
156 if(childByRole
!= null) {
157 final TextRange textRange
= childByRole
.getTextRange();
158 if (length
== -1) length
= textRange
.getLength();
159 final int startOffset
= textRange
.getStartOffset() + offset
;
161 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
163 childByRole
, startOffset
, startOffset
+ length
,
164 localizedMessage
, HighlightInfo
.htmlEscapeToolTip(localizedMessage
)
167 if (highlightInfo
== null) {
168 highlightInfo
= HighlightInfo
.createHighlightInfo(
170 new TextRange(startOffset
, startOffset
+ length
),
172 localizedMessage
, NONEMPTY_TEXT_ATTRIBUTES
176 for (final IntentionAction quickFixAction
: quickFixActions
) {
177 if (quickFixAction
== null) continue;
178 QuickFixAction
.registerQuickFixAction(highlightInfo
, textRange
, quickFixAction
, null);
180 addToResults(highlightInfo
);
184 private void checkTagByDescriptor(final XmlTag tag
) {
185 String name
= tag
.getName();
187 XmlElementDescriptor elementDescriptor
= null;
189 final PsiElement parent
= tag
.getParent();
190 if (parent
instanceof XmlTag
) {
191 XmlTag parentTag
= (XmlTag
)parent
;
192 final XmlElementDescriptor parentDescriptor
= parentTag
.getDescriptor();
194 if (parentDescriptor
!= null) {
195 elementDescriptor
= XmlExtension
.getExtension(tag
.getContainingFile()).getElementDescriptor(tag
, parentTag
, parentDescriptor
);
198 if (parentDescriptor
!= null &&
199 elementDescriptor
== null &&
200 parentTag
.getUserData(DO_NOT_VALIDATE_KEY
) == null &&
201 !XmlUtil
.tagFromTemplateFramework(tag
)
203 if (tag
instanceof HtmlTag
) {
204 //XmlEntitiesInspection inspection = getInspectionProfile(tag, HtmlStyleLocalInspection.SHORT_NAME);
205 //if (inspection != null /*&& isAdditionallyDeclared(inspection.getAdditionalEntries(XmlEntitiesInspection.UNKNOWN_TAG), name)*/) {
212 XmlErrorMessages
.message("element.is.not.allowed.here", name
),
213 getTagProblemInfoType(tag
),
219 if (elementDescriptor
instanceof AnyXmlElementDescriptor
||
220 elementDescriptor
== null
222 elementDescriptor
= tag
.getDescriptor();
225 if (elementDescriptor
== null) return;
229 elementDescriptor
= tag
.getDescriptor();
231 if (elementDescriptor
== null) {
232 addElementsForTag(tag
, XmlErrorMessages
.message("element.must.be.declared", name
), HighlightInfoType
.WRONG_REF
, null);
237 XmlAttributeDescriptor
[] attributeDescriptors
= elementDescriptor
.getAttributesDescriptors(tag
);
238 Set
<String
> requiredAttributes
= null;
240 for (XmlAttributeDescriptor attribute
: attributeDescriptors
) {
241 if (attribute
!= null && attribute
.isRequired()) {
242 if (requiredAttributes
== null) {
243 requiredAttributes
= new HashSet
<String
>();
245 requiredAttributes
.add(attribute
.getName(tag
));
249 if (requiredAttributes
!= null) {
250 for (final String attrName
: requiredAttributes
) {
251 if (tag
.getAttribute(attrName
, "") == null &&
252 !XmlExtension
.getExtension(tag
.getContainingFile()).isRequiredAttributeImplicitlyPresent(tag
, attrName
)) {
254 final InsertRequiredAttributeFix insertRequiredAttributeIntention
= new InsertRequiredAttributeFix(
255 tag
, attrName
, null);
256 final String localizedMessage
= XmlErrorMessages
.message("element.doesnt.have.required.attribute", name
, attrName
);
257 final InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(tag
.getProject()).getInspectionProfile();
258 final LocalInspectionToolWrapper toolWrapper
=
259 (LocalInspectionToolWrapper
)profile
.getInspectionTool(RequiredAttributesInspection
.SHORT_NAME
, tag
);
260 if (toolWrapper
!= null) {
261 RequiredAttributesInspection inspection
= (RequiredAttributesInspection
)toolWrapper
.getTool();
266 insertRequiredAttributeIntention
,
267 HighlightDisplayKey
.find(RequiredAttributesInspection
.SHORT_NAME
),
269 XmlEntitiesInspection
.NOT_REQUIRED_ATTRIBUTE
276 if (elementDescriptor
instanceof Validator
) {
277 //noinspection unchecked
278 ((Validator
<XmlTag
>)elementDescriptor
).validate(tag
,this);
282 private void reportOneTagProblem(final XmlTag tag
,
284 final String localizedMessage
,
285 final IntentionAction basicIntention
,
286 final HighlightDisplayKey key
,
287 final XmlEntitiesInspection inspection
,
289 boolean htmlTag
= false;
291 if (tag
instanceof HtmlTag
) {
293 if(isAdditionallyDeclared(inspection
.getAdditionalEntries(type
), name
)) return;
296 final InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(tag
.getProject()).getInspectionProfile();
297 final IntentionAction intentionAction
= inspection
.getIntentionAction(name
, type
);
298 if (htmlTag
&& profile
.isToolEnabled(key
, tag
)) {
299 addElementsForTagWithManyQuickFixes(
302 isInjectedHtmlTagForWhichNoProblemsReporting((HtmlTag
)tag
) ?
303 HighlightInfoType
.INFORMATION
:
304 SeverityRegistrar
.getInstance(tag
.getProject()).getHighlightInfoTypeBySeverity(profile
.getErrorLevel(key
, tag
).getSeverity()),
307 } else if (!htmlTag
) {
311 HighlightInfoType
.ERROR
,
317 private static boolean isAdditionallyDeclared(final String additional
, String name
) {
318 name
= name
.toLowerCase();
319 if (!additional
.contains(name
)) return false;
321 StringTokenizer tokenizer
= new StringTokenizer(additional
, ", ");
322 while (tokenizer
.hasMoreTokens()) {
323 if (name
.equals(tokenizer
.nextToken())) {
331 private static HighlightInfoType
getTagProblemInfoType(XmlTag tag
) {
332 if (tag
instanceof HtmlTag
&& XmlUtil
.HTML_URI
.equals(tag
.getNamespace())) {
333 if (isInjectedHtmlTagForWhichNoProblemsReporting((HtmlTag
)tag
)) return HighlightInfoType
.INFORMATION
;
334 return HighlightInfoType
.WARNING
;
336 return HighlightInfoType
.WRONG_REF
;
339 private static boolean isInjectedHtmlTagForWhichNoProblemsReporting(HtmlTag tag
) {
340 PsiElement context
= tag
.getContainingFile().getContext();
341 if (context
!= null && skipValidation(context
)) return true;
345 private static boolean skipValidation(PsiElement context
) {
346 return context
.getUserData(DO_NOT_VALIDATE_KEY
) != null;
349 public static void setSkipValidation(@NotNull PsiElement element
) {
350 element
.putUserData(DO_NOT_VALIDATE_KEY
, "");
353 @Override public void visitXmlAttribute(XmlAttribute attribute
) {}
355 private void checkAttribute(XmlAttribute attribute
) {
356 XmlTag tag
= attribute
.getParent();
358 if (attribute
.isNamespaceDeclaration()) {
359 checkReferences(attribute
.getValueElement());
362 final String namespace
= attribute
.getNamespace();
364 if (XmlUtil
.XML_SCHEMA_INSTANCE_URI
.equals(namespace
)) {
365 checkReferences(attribute
.getValueElement());
369 XmlElementDescriptor elementDescriptor
= tag
.getDescriptor();
370 if (elementDescriptor
== null ||
371 elementDescriptor
instanceof AnyXmlElementDescriptor
||
376 XmlAttributeDescriptor attributeDescriptor
= elementDescriptor
.getAttributeDescriptor(attribute
);
378 final String name
= attribute
.getName();
380 if (attributeDescriptor
== null) {
381 if (!XmlUtil
.attributeFromTemplateFramework(name
, tag
)) {
382 final String localizedMessage
= XmlErrorMessages
.message("attribute.is.not.allowed.here", name
);
383 final HighlightInfo highlightInfo
= reportAttributeProblem(tag
, name
, attribute
, localizedMessage
);
384 if (highlightInfo
!= null) {
385 final XmlFile xmlFile
= (XmlFile
)tag
.getContainingFile();
386 if (xmlFile
!= null) {
387 XmlExtension
.getExtension(xmlFile
).createAddAttributeFix(attribute
, highlightInfo
);
393 checkDuplicateAttribute(tag
, attribute
);
395 if (tag
instanceof HtmlTag
&&
396 attribute
.getValueElement() == null &&
397 !HtmlUtil
.isSingleHtmlAttribute(name
)
399 final String localizedMessage
= XmlErrorMessages
.message("empty.attribute.is.not.allowed", name
);
400 reportAttributeProblem(tag
, name
, attribute
, localizedMessage
);
403 // we skip resolve of attribute references since there is separate check when taking attribute descriptors
404 PsiReference
[] attrRefs
= attribute
.getReferences();
405 doCheckRefs(attribute
, attrRefs
, attribute
.getNamespacePrefix().length() > 0 ?
2 : 1);
410 private HighlightInfo
reportAttributeProblem(final XmlTag tag
,
411 final String localName
,
412 final XmlAttribute attribute
,
413 final String localizedMessage
) {
415 final RemoveAttributeIntentionFix removeAttributeIntention
= new RemoveAttributeIntentionFix(localName
,attribute
);
417 if (!(tag
instanceof HtmlTag
)) {
418 final HighlightInfoType tagProblemInfoType
= HighlightInfoType
.WRONG_REF
;
420 final ASTNode node
= SourceTreeToPsiMap
.psiElementToTree(attribute
);
422 final ASTNode child
= XmlChildRole
.ATTRIBUTE_NAME_FINDER
.findChild(node
);
423 assert child
!= null;
424 final HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
425 tagProblemInfoType
, child
,
428 addToResults(highlightInfo
);
430 QuickFixAction
.registerQuickFixAction(highlightInfo
, removeAttributeIntention
);
432 return highlightInfo
;
438 private void checkDuplicateAttribute(XmlTag tag
, final XmlAttribute attribute
) {
439 if (skipValidation(tag
)) {
443 final XmlAttribute
[] attributes
= tag
.getAttributes();
444 final PsiFile containingFile
= tag
.getContainingFile();
445 final XmlExtension extension
= containingFile
instanceof XmlFile ?
446 XmlExtension
.getExtension(containingFile
) :
447 XmlExtension
.DEFAULT_EXTENSION
;
448 for (XmlAttribute tagAttribute
: attributes
) {
449 ProgressManager
.checkCanceled();
450 if (attribute
!= tagAttribute
&& Comparing
.strEqual(attribute
.getName(), tagAttribute
.getName())) {
451 final String localName
= attribute
.getLocalName();
453 if (extension
.canBeDuplicated(tagAttribute
)) continue; // multiple import attributes are allowed in jsp directive
455 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
456 getTagProblemInfoType(tag
),
457 XmlChildRole
.ATTRIBUTE_NAME_FINDER
.findChild(SourceTreeToPsiMap
.psiElementToTree(attribute
)),
458 XmlErrorMessages
.message("duplicate.attribute", localName
));
459 addToResults(highlightInfo
);
461 IntentionAction intentionAction
= new RemoveAttributeIntentionFix(localName
, attribute
);
463 QuickFixAction
.registerQuickFixAction(highlightInfo
, intentionAction
);
468 @Override public void visitXmlDocument(final XmlDocument document
) {
469 if (document
.getLanguage() == DTDLanguage
.INSTANCE
) {
470 final PsiMetaData psiMetaData
= document
.getMetaData();
471 if (psiMetaData
instanceof Validator
) {
472 //noinspection unchecked
473 ((Validator
<XmlDocument
>)psiMetaData
).validate(document
, this);
478 @Override public void visitXmlTag(XmlTag tag
) {
481 @Override public void visitXmlAttributeValue(XmlAttributeValue value
) {
482 final PsiElement parent
= value
.getParent();
483 if (!(parent
instanceof XmlAttribute
)) {
484 checkReferences(value
);
488 XmlAttribute attribute
= (XmlAttribute
)parent
;
490 XmlTag tag
= attribute
.getParent();
492 XmlElementDescriptor elementDescriptor
= tag
.getDescriptor();
493 XmlAttributeDescriptor attributeDescriptor
= elementDescriptor
!= null ? elementDescriptor
.getAttributeDescriptor(attribute
):null;
495 if (attributeDescriptor
!= null && value
.getUserData(DO_NOT_VALIDATE_KEY
) == null) {
496 String error
= attributeDescriptor
.validateValue(value
, attribute
.getValue());
499 addToResults(HighlightInfo
.createHighlightInfo(
500 getTagProblemInfoType(tag
),
507 checkReferences(value
);
510 private void checkReferences(PsiElement value
) {
511 if (value
== null) return;
513 doCheckRefs(value
, value
.getReferences());
516 private void doCheckRefs(final PsiElement value
, final PsiReference
[] references
) {
517 doCheckRefs(value
, references
, 0);
520 private void doCheckRefs(final PsiElement value
, final PsiReference
[] references
, int start
) {
521 for (int i
= start
; i
< references
.length
; ++i
) {
522 PsiReference reference
= references
[i
];
523 ProgressManager
.checkCanceled();
524 if (reference
== null) {
527 if (!reference
.isSoft()) {
528 if(hasBadResolve(reference
)) {
529 String description
= getErrorDescription(reference
);
531 final int startOffset
= reference
.getElement().getTextRange().getStartOffset();
532 final TextRange referenceRange
= reference
.getRangeInElement();
534 // logging for IDEADEV-29655
535 if (referenceRange
.getStartOffset() > referenceRange
.getEndOffset()) {
536 LOG
.error("Reference range start offset > end offset: " + reference
+
537 ", start offset: " + referenceRange
.getStartOffset() + ", end offset: " + referenceRange
.getEndOffset());
540 HighlightInfo info
= HighlightInfo
.createHighlightInfo(
541 getTagProblemInfoType(PsiTreeUtil
.getParentOfType(value
, XmlTag
.class)),
542 startOffset
+ referenceRange
.getStartOffset(),
543 startOffset
+ referenceRange
.getEndOffset(),
547 if (reference
instanceof QuickFixProvider
) ((QuickFixProvider
)reference
).registerQuickfix(info
, reference
);
548 UnresolvedReferenceQuickFixProvider
.registerReferenceFixes(reference
, new QuickFixActionRegistrarImpl(info
));
554 public static String
getErrorDescription(final PsiReference reference
) {
556 if (reference
instanceof EmptyResolveMessageProvider
) {
557 message
= ((EmptyResolveMessageProvider
)reference
).getUnresolvedMessagePattern();
560 //noinspection UnresolvedPropertyKey
561 message
= PsiBundle
.message("cannot.resolve.symbol");
566 description
= MessageFormat
.format(message
, reference
.getCanonicalText());
567 } catch(IllegalArgumentException ex
) {
568 // unresolvedMessage provided by third-party reference contains wrong format string (e.g. {}), tolerate it
569 description
= message
;
570 LOG
.warn(XmlErrorMessages
.message("plugin.reference.message.problem",reference
.getClass().getName(),message
));
575 public static boolean hasBadResolve(final PsiReference reference
) {
576 if (reference
instanceof PsiPolyVariantReference
) {
577 return ((PsiPolyVariantReference
)reference
).multiResolve(false).length
== 0;
579 return reference
.resolve() == null;
582 @Override public void visitXmlDoctype(XmlDoctype xmlDoctype
) {
583 if (skipValidation(xmlDoctype
)) return;
584 checkReferences(xmlDoctype
);
587 private void addToResults(final HighlightInfo info
) {
588 if (myResult
== null) myResult
= new SmartList
<HighlightInfo
>();
592 public static void setDoJaxpTesting(boolean doJaxpTesting
) {
593 ourDoJaxpTesting
= doJaxpTesting
;
596 public void addMessage(PsiElement context
, String message
, int type
) {
597 if (message
!= null && message
.length() > 0) {
598 if (context
instanceof XmlTag
&& XmlExtension
.getExtension(context
.getContainingFile()).shouldBeHighlightedAsTag((XmlTag
)context
)) {
599 addElementsForTag((XmlTag
)context
, message
, type
== ERROR ? HighlightInfoType
.ERROR
: type
== WARNING ? HighlightInfoType
.WARNING
: HighlightInfoType
.INFO
, null);
602 addToResults(HighlightInfo
.createHighlightInfo(HighlightInfoType
.WRONG_REF
, context
, message
));
607 public void addMessage(final PsiElement context
, final String message
, final ErrorType type
, final IntentionAction
... fixes
) {
608 if (message
!= null && message
.length() > 0) {
609 final PsiFile containingFile
= context
.getContainingFile();
610 final HighlightInfoType defaultInfoType
= type
== ErrorType
.ERROR ? HighlightInfoType
.ERROR
: type
== ErrorType
.WARNING ? HighlightInfoType
.WARNING
: HighlightInfoType
.INFO
;
612 if (context
instanceof XmlTag
&& XmlExtension
.getExtension(containingFile
).shouldBeHighlightedAsTag((XmlTag
)context
)) {
613 addElementsForTagWithManyQuickFixes((XmlTag
)context
, message
, defaultInfoType
, fixes
);
616 final PsiElement contextOfFile
= containingFile
.getContext();
617 final HighlightInfo highlightInfo
;
619 if (contextOfFile
!= null) {
620 final int offsetInRealDocument
= PsiUtilBase
.findInjectedElementOffsetInRealDocument(context
);
621 highlightInfo
= HighlightInfo
.createHighlightInfo(defaultInfoType
, context
.getTextRange().shiftRight(offsetInRealDocument
), message
);
623 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.WRONG_REF
, context
, message
);
627 for (final IntentionAction quickFixAction
: fixes
) {
628 if (quickFixAction
== null) continue;
629 QuickFixAction
.registerQuickFixAction(highlightInfo
, quickFixAction
);
632 addToResults(highlightInfo
);
637 public static void visitJspElement(OuterLanguageElement text
) {
638 PsiElement parent
= text
.getParent();
640 if (parent
instanceof XmlText
) {
641 parent
= parent
.getParent();
644 parent
.putUserData(DO_NOT_VALIDATE_KEY
, "");
647 public boolean suitableForFile(final PsiFile file
) {
648 return file
instanceof XmlFile
;
651 public void visit(final PsiElement element
, final HighlightInfoHolder holder
) {
652 element
.accept(this);
654 List
<HighlightInfo
> result
= myResult
;
655 holder
.addAll(result
);
659 public boolean analyze(Runnable action
, final boolean updateWholeFile
, final PsiFile file
) {
669 public HighlightVisitor
clone() {
670 return new XmlHighlightVisitor();
677 public static String
getUnquotedValue(XmlAttributeValue value
, XmlTag tag
) {
678 String unquotedValue
= StringUtil
.stripQuotesAroundValue(value
.getText());
680 if (tag
instanceof HtmlTag
) {
681 unquotedValue
= unquotedValue
.toLowerCase();
684 return unquotedValue
;