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
.intention
.IntentionAction
;
25 import com
.intellij
.codeInspection
.InspectionProfile
;
26 import com
.intellij
.codeInspection
.ex
.LocalInspectionToolWrapper
;
27 import com
.intellij
.codeInspection
.htmlInspections
.RequiredAttributesInspection
;
28 import com
.intellij
.codeInspection
.htmlInspections
.XmlEntitiesInspection
;
29 import com
.intellij
.idea
.LoggerFactory
;
30 import com
.intellij
.lang
.ASTNode
;
31 import com
.intellij
.lang
.dtd
.DTDLanguage
;
32 import com
.intellij
.openapi
.diagnostic
.Logger
;
33 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
34 import com
.intellij
.openapi
.progress
.ProgressManager
;
35 import com
.intellij
.openapi
.util
.Comparing
;
36 import com
.intellij
.openapi
.util
.Key
;
37 import com
.intellij
.openapi
.util
.TextRange
;
38 import com
.intellij
.openapi
.util
.text
.StringUtil
;
39 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
40 import com
.intellij
.psi
.*;
41 import com
.intellij
.psi
.html
.HtmlTag
;
42 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
43 import com
.intellij
.psi
.meta
.PsiMetaData
;
44 import com
.intellij
.psi
.templateLanguages
.OuterLanguageElement
;
45 import com
.intellij
.psi
.util
.PsiTreeUtil
;
46 import com
.intellij
.psi
.util
.PsiUtilBase
;
47 import com
.intellij
.psi
.xml
.*;
48 import com
.intellij
.util
.SmartList
;
49 import com
.intellij
.xml
.XmlAttributeDescriptor
;
50 import com
.intellij
.xml
.XmlElementDescriptor
;
51 import com
.intellij
.xml
.XmlExtension
;
52 import com
.intellij
.xml
.impl
.schema
.AnyXmlElementDescriptor
;
53 import com
.intellij
.xml
.util
.HtmlUtil
;
54 import com
.intellij
.xml
.util
.XmlTagUtil
;
55 import com
.intellij
.xml
.util
.XmlUtil
;
56 import org
.jetbrains
.annotations
.Nullable
;
58 import java
.text
.MessageFormat
;
59 import java
.util
.HashSet
;
60 import java
.util
.List
;
62 import java
.util
.StringTokenizer
;
67 public class XmlHighlightVisitor
extends XmlElementVisitor
implements HighlightVisitor
, Validator
.ValidationHost
{
68 private static final Logger LOG
= LoggerFactory
.getInstance().getLoggerInstance("com.intellij.codeInsight.daemon.impl.analysis.XmlHighlightVisitor");
69 public static final Key
<String
> DO_NOT_VALIDATE_KEY
= Key
.create("do not validate");
70 private List
<HighlightInfo
> myResult
;
72 private static boolean ourDoJaxpTesting
;
74 private static final TextAttributes NONEMPTY_TEXT_ATTRIBUTES
= new TextAttributes() {
75 public boolean isEmpty() {
80 private void addElementsForTag(XmlTag tag
,
81 String localizedMessage
,
82 HighlightInfoType type
,
83 IntentionAction quickFixAction
) {
84 addElementsForTagWithManyQuickFixes(tag
, localizedMessage
, type
, quickFixAction
);
87 private void addElementsForTagWithManyQuickFixes(XmlTag tag
,
88 String localizedMessage
,
89 HighlightInfoType type
, IntentionAction
... quickFixActions
) {
90 bindMessageToTag(tag
, type
, -1, localizedMessage
, quickFixActions
);
93 @Override public void visitXmlToken(XmlToken token
) {
94 if (token
.getTokenType() == XmlTokenType
.XML_NAME
|| token
.getTokenType() == XmlTokenType
.XML_TAG_NAME
) {
95 PsiElement element
= token
.getPrevSibling();
96 while(element
instanceof PsiWhiteSpace
) element
= element
.getPrevSibling();
98 if (element
instanceof XmlToken
) {
99 if (((XmlToken
)element
).getTokenType() == XmlTokenType
.XML_START_TAG_START
) {
100 PsiElement parent
= element
.getParent();
102 if (parent
instanceof XmlTag
&& !(token
.getNextSibling() instanceof OuterLanguageElement
)) {
103 checkTag((XmlTag
)parent
);
107 PsiElement parent
= token
.getParent();
109 if (parent
instanceof XmlAttribute
&& !(token
.getNextSibling() instanceof OuterLanguageElement
)) {
110 checkAttribute((XmlAttribute
) parent
);
116 private void checkTag(XmlTag tag
) {
117 if (ourDoJaxpTesting
) return;
119 if (myResult
== null) {
120 checkTagByDescriptor(tag
);
123 if (myResult
== null) {
124 if (tag
.getUserData(DO_NOT_VALIDATE_KEY
) == null) {
125 final XmlElementDescriptor descriptor
= tag
.getDescriptor();
127 if (tag
instanceof HtmlTag
&&
128 ( descriptor
instanceof AnyXmlElementDescriptor
||
135 checkReferences(tag
);
140 private void bindMessageToTag(final XmlTag tag
, final HighlightInfoType warning
, final int messageLength
, final String localizedMessage
, IntentionAction
... quickFixActions
) {
141 XmlToken childByRole
= XmlTagUtil
.getStartTagNameElement(tag
);
143 bindMessageToAstNode(childByRole
, warning
, 0, messageLength
, localizedMessage
, quickFixActions
);
144 childByRole
= XmlTagUtil
.getEndTagNameElement(tag
);
145 bindMessageToAstNode(childByRole
, warning
, 0, messageLength
, localizedMessage
, quickFixActions
);
148 private void bindMessageToAstNode(final PsiElement childByRole
,
149 final HighlightInfoType warning
,
152 final String localizedMessage
, IntentionAction
... quickFixActions
) {
153 if(childByRole
!= null) {
154 final TextRange textRange
= childByRole
.getTextRange();
155 if (length
== -1) length
= textRange
.getLength();
156 final int startOffset
= textRange
.getStartOffset() + offset
;
158 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
160 childByRole
, startOffset
, startOffset
+ length
,
161 localizedMessage
, HighlightInfo
.htmlEscapeToolTip(localizedMessage
)
164 if (highlightInfo
== null) {
165 highlightInfo
= HighlightInfo
.createHighlightInfo(
167 new TextRange(startOffset
, startOffset
+ length
),
169 localizedMessage
, NONEMPTY_TEXT_ATTRIBUTES
173 for (final IntentionAction quickFixAction
: quickFixActions
) {
174 if (quickFixAction
== null) continue;
175 QuickFixAction
.registerQuickFixAction(highlightInfo
, textRange
, quickFixAction
, null);
177 addToResults(highlightInfo
);
181 private void checkTagByDescriptor(final XmlTag tag
) {
182 String name
= tag
.getName();
184 XmlElementDescriptor elementDescriptor
= null;
186 final PsiElement parent
= tag
.getParent();
187 if (parent
instanceof XmlTag
) {
188 XmlTag parentTag
= (XmlTag
)parent
;
189 final XmlElementDescriptor parentDescriptor
= parentTag
.getDescriptor();
191 if (parentDescriptor
!= null) {
192 elementDescriptor
= XmlExtension
.getExtension(tag
.getContainingFile()).getElementDescriptor(tag
, parentTag
, parentDescriptor
);
195 if (parentDescriptor
!= null &&
196 elementDescriptor
== null &&
197 parentTag
.getUserData(DO_NOT_VALIDATE_KEY
) == null &&
198 !XmlUtil
.tagFromTemplateFramework(tag
)
200 if (tag
instanceof HtmlTag
) {
201 //XmlEntitiesInspection inspection = getInspectionProfile(tag, HtmlStyleLocalInspection.SHORT_NAME);
202 //if (inspection != null /*&& isAdditionallyDeclared(inspection.getAdditionalEntries(XmlEntitiesInspection.UNKNOWN_TAG), name)*/) {
209 XmlErrorMessages
.message("element.is.not.allowed.here", name
),
210 getTagProblemInfoType(tag
),
216 if (elementDescriptor
instanceof AnyXmlElementDescriptor
||
217 elementDescriptor
== null
219 elementDescriptor
= tag
.getDescriptor();
222 if (elementDescriptor
== null) return;
226 elementDescriptor
= tag
.getDescriptor();
228 if (elementDescriptor
== null) {
229 addElementsForTag(tag
, XmlErrorMessages
.message("element.must.be.declared", name
), HighlightInfoType
.WRONG_REF
, null);
234 XmlAttributeDescriptor
[] attributeDescriptors
= elementDescriptor
.getAttributesDescriptors(tag
);
235 Set
<String
> requiredAttributes
= null;
237 for (XmlAttributeDescriptor attribute
: attributeDescriptors
) {
238 if (attribute
!= null && attribute
.isRequired()) {
239 if (requiredAttributes
== null) {
240 requiredAttributes
= new HashSet
<String
>();
242 requiredAttributes
.add(attribute
.getName(tag
));
246 if (requiredAttributes
!= null) {
247 for (final String attrName
: requiredAttributes
) {
248 if (tag
.getAttribute(attrName
, "") == null &&
249 !XmlExtension
.getExtension(tag
.getContainingFile()).isRequiredAttributeImplicitlyPresent(tag
, attrName
)) {
251 final InsertRequiredAttributeFix insertRequiredAttributeIntention
= new InsertRequiredAttributeFix(
252 tag
, attrName
, null);
253 final String localizedMessage
= XmlErrorMessages
.message("element.doesnt.have.required.attribute", name
, attrName
);
254 final InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(tag
.getProject()).getInspectionProfile();
255 final LocalInspectionToolWrapper toolWrapper
=
256 (LocalInspectionToolWrapper
)profile
.getInspectionTool(RequiredAttributesInspection
.SHORT_NAME
, tag
);
257 if (toolWrapper
!= null) {
258 RequiredAttributesInspection inspection
= (RequiredAttributesInspection
)toolWrapper
.getTool();
263 insertRequiredAttributeIntention
,
264 HighlightDisplayKey
.find(RequiredAttributesInspection
.SHORT_NAME
),
266 XmlEntitiesInspection
.NOT_REQUIRED_ATTRIBUTE
273 if (elementDescriptor
instanceof Validator
) {
274 //noinspection unchecked
275 ((Validator
<XmlTag
>)elementDescriptor
).validate(tag
,this);
279 private void reportOneTagProblem(final XmlTag tag
,
281 final String localizedMessage
,
282 final IntentionAction basicIntention
,
283 final HighlightDisplayKey key
,
284 final XmlEntitiesInspection inspection
,
286 boolean htmlTag
= false;
288 if (tag
instanceof HtmlTag
) {
290 if(isAdditionallyDeclared(inspection
.getAdditionalEntries(type
), name
)) return;
293 final InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(tag
.getProject()).getInspectionProfile();
294 final IntentionAction intentionAction
= inspection
.getIntentionAction(name
, type
);
295 if (htmlTag
&& profile
.isToolEnabled(key
, tag
)) {
296 addElementsForTagWithManyQuickFixes(
299 SeverityRegistrar
.getInstance(tag
.getProject()).getHighlightInfoTypeBySeverity(profile
.getErrorLevel(key
, tag
).getSeverity()),
302 } else if (!htmlTag
) {
306 HighlightInfoType
.ERROR
,
312 private static boolean isAdditionallyDeclared(final String additional
, String name
) {
313 name
= name
.toLowerCase();
314 if (!additional
.contains(name
)) return false;
316 StringTokenizer tokenizer
= new StringTokenizer(additional
, ", ");
317 while (tokenizer
.hasMoreTokens()) {
318 if (name
.equals(tokenizer
.nextToken())) {
326 private static HighlightInfoType
getTagProblemInfoType(XmlTag tag
) {
327 return tag
instanceof HtmlTag
&& XmlUtil
.HTML_URI
.equals(tag
.getNamespace()) ? HighlightInfoType
.WARNING
: HighlightInfoType
.WRONG_REF
;
330 @Override public void visitXmlAttribute(XmlAttribute attribute
) {}
332 private void checkAttribute(XmlAttribute attribute
) {
333 XmlTag tag
= attribute
.getParent();
335 if (attribute
.isNamespaceDeclaration()) {
336 checkReferences(attribute
.getValueElement());
339 final String namespace
= attribute
.getNamespace();
341 if (XmlUtil
.XML_SCHEMA_INSTANCE_URI
.equals(namespace
)) {
342 checkReferences(attribute
.getValueElement());
346 XmlElementDescriptor elementDescriptor
= tag
.getDescriptor();
347 if (elementDescriptor
== null ||
348 elementDescriptor
instanceof AnyXmlElementDescriptor
||
353 XmlAttributeDescriptor attributeDescriptor
= elementDescriptor
.getAttributeDescriptor(attribute
);
355 final String name
= attribute
.getName();
357 if (attributeDescriptor
== null) {
358 if (!XmlUtil
.attributeFromTemplateFramework(name
, tag
)) {
359 final String localizedMessage
= XmlErrorMessages
.message("attribute.is.not.allowed.here", name
);
360 final HighlightInfo highlightInfo
= reportAttributeProblem(tag
, name
, attribute
, localizedMessage
);
361 if (highlightInfo
!= null) {
362 final XmlFile xmlFile
= (XmlFile
)tag
.getContainingFile();
363 if (xmlFile
!= null) {
364 XmlExtension
.getExtension(xmlFile
).createAddAttributeFix(attribute
, highlightInfo
);
370 checkDuplicateAttribute(tag
, attribute
);
372 if (tag
instanceof HtmlTag
&&
373 attribute
.getValueElement() == null &&
374 !HtmlUtil
.isSingleHtmlAttribute(name
)
376 final String localizedMessage
= XmlErrorMessages
.message("empty.attribute.is.not.allowed", name
);
377 reportAttributeProblem(tag
, name
, attribute
, localizedMessage
);
380 doCheckRefs(attribute
, attribute
.getReferences(), 1);
385 private HighlightInfo
reportAttributeProblem(final XmlTag tag
,
386 final String localName
,
387 final XmlAttribute attribute
,
388 final String localizedMessage
) {
390 final RemoveAttributeIntentionFix removeAttributeIntention
= new RemoveAttributeIntentionFix(localName
,attribute
);
392 if (!(tag
instanceof HtmlTag
)) {
393 final HighlightInfoType tagProblemInfoType
= HighlightInfoType
.WRONG_REF
;
394 IntentionAction
[] quickFixes
= new IntentionAction
[]{removeAttributeIntention
};
396 final ASTNode node
= SourceTreeToPsiMap
.psiElementToTree(attribute
);
398 final ASTNode child
= XmlChildRole
.ATTRIBUTE_NAME_FINDER
.findChild(node
);
399 assert child
!= null;
400 final HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
401 tagProblemInfoType
, child
,
404 addToResults(highlightInfo
);
406 for (IntentionAction quickFix
: quickFixes
) {
407 QuickFixAction
.registerQuickFixAction(highlightInfo
, quickFix
);
410 return highlightInfo
;
416 private void checkDuplicateAttribute(XmlTag tag
, final XmlAttribute attribute
) {
417 if (tag
.getUserData(DO_NOT_VALIDATE_KEY
) != null) {
421 final XmlAttribute
[] attributes
= tag
.getAttributes();
422 ProgressManager progressManager
= ProgressManager
.getInstance();
423 final PsiFile containingFile
= tag
.getContainingFile();
424 final XmlExtension extension
= containingFile
instanceof XmlFile ?
425 XmlExtension
.getExtension(containingFile
) :
426 XmlExtension
.DEFAULT_EXTENSION
;
427 for (XmlAttribute tagAttribute
: attributes
) {
428 progressManager
.checkCanceled();
429 if (attribute
!= tagAttribute
&& Comparing
.strEqual(attribute
.getName(), tagAttribute
.getName())) {
430 final String localName
= attribute
.getLocalName();
432 if (extension
.canBeDuplicated(tagAttribute
)) continue; // multiple import attributes are allowed in jsp directive
434 HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(
435 getTagProblemInfoType(tag
),
436 XmlChildRole
.ATTRIBUTE_NAME_FINDER
.findChild(SourceTreeToPsiMap
.psiElementToTree(attribute
)),
437 XmlErrorMessages
.message("duplicate.attribute", localName
));
438 addToResults(highlightInfo
);
440 IntentionAction intentionAction
= new RemoveAttributeIntentionFix(localName
, attribute
);
442 QuickFixAction
.registerQuickFixAction(highlightInfo
, intentionAction
);
447 @Override public void visitXmlDocument(final XmlDocument document
) {
448 if (document
.getLanguage() == DTDLanguage
.INSTANCE
) {
449 final PsiMetaData psiMetaData
= document
.getMetaData();
450 if (psiMetaData
instanceof Validator
) {
451 //noinspection unchecked
452 ((Validator
<XmlDocument
>)psiMetaData
).validate(document
, this);
457 @Override public void visitXmlTag(XmlTag tag
) {
460 @Override public void visitXmlAttributeValue(XmlAttributeValue value
) {
461 final PsiElement parent
= value
.getParent();
462 if (!(parent
instanceof XmlAttribute
)) {
463 checkReferences(value
);
467 XmlAttribute attribute
= (XmlAttribute
)parent
;
469 XmlTag tag
= attribute
.getParent();
471 XmlElementDescriptor elementDescriptor
= tag
.getDescriptor();
472 XmlAttributeDescriptor attributeDescriptor
= elementDescriptor
!= null ? elementDescriptor
.getAttributeDescriptor(attribute
):null;
474 if (attributeDescriptor
!= null && value
.getUserData(DO_NOT_VALIDATE_KEY
) == null) {
475 String error
= attributeDescriptor
.validateValue(value
, attribute
.getValue());
478 addToResults(HighlightInfo
.createHighlightInfo(
479 getTagProblemInfoType(tag
),
486 checkReferences(value
);
489 private void checkReferences(PsiElement value
) {
490 if (value
== null) return;
492 doCheckRefs(value
, value
.getReferences());
495 private void doCheckRefs(final PsiElement value
, final PsiReference
[] references
) {
496 doCheckRefs(value
, references
, 0);
499 private void doCheckRefs(final PsiElement value
, final PsiReference
[] references
, int start
) {
500 ProgressManager progressManager
= ProgressManager
.getInstance();
501 for (int i
= start
; i
< references
.length
; ++i
) {
502 PsiReference reference
= references
[i
];
503 progressManager
.checkCanceled();
504 if (reference
== null) {
507 if (!reference
.isSoft()) {
508 if(hasBadResolve(reference
)) {
509 String description
= getErrorDescription(reference
);
511 final int startOffset
= reference
.getElement().getTextRange().getStartOffset();
512 final TextRange referenceRange
= reference
.getRangeInElement();
514 // logging for IDEADEV-29655
515 if (referenceRange
.getStartOffset() > referenceRange
.getEndOffset()) {
516 LOG
.error("Reference range start offset > end offset: " + reference
+
517 ", start offset: " + referenceRange
.getStartOffset() + ", end offset: " + referenceRange
.getEndOffset());
520 HighlightInfo info
= HighlightInfo
.createHighlightInfo(
521 getTagProblemInfoType(PsiTreeUtil
.getParentOfType(value
, XmlTag
.class)),
522 startOffset
+ referenceRange
.getStartOffset(),
523 startOffset
+ referenceRange
.getEndOffset(),
527 if (reference
instanceof QuickFixProvider
) ((QuickFixProvider
)reference
).registerQuickfix(info
, reference
);
533 public static String
getErrorDescription(final PsiReference reference
) {
535 if (reference
instanceof EmptyResolveMessageProvider
) {
536 message
= ((EmptyResolveMessageProvider
)reference
).getUnresolvedMessagePattern();
539 //noinspection UnresolvedPropertyKey
540 message
= PsiBundle
.message("cannot.resolve.symbol");
545 description
= MessageFormat
.format(message
, reference
.getCanonicalText());
546 } catch(IllegalArgumentException ex
) {
547 // unresolvedMessage provided by third-party reference contains wrong format string (e.g. {}), tolerate it
548 description
= message
;
549 LOG
.warn(XmlErrorMessages
.message("plugin.reference.message.problem",reference
.getClass().getName(),message
));
554 public static boolean hasBadResolve(final PsiReference reference
) {
555 if (reference
instanceof PsiPolyVariantReference
) {
556 return ((PsiPolyVariantReference
)reference
).multiResolve(false).length
== 0;
558 return reference
.resolve() == null;
561 @Override public void visitXmlDoctype(XmlDoctype xmlDoctype
) {
562 if (xmlDoctype
.getUserData(DO_NOT_VALIDATE_KEY
) != null) return;
563 checkReferences(xmlDoctype
);
566 private void addToResults(final HighlightInfo info
) {
567 if (myResult
== null) myResult
= new SmartList
<HighlightInfo
>();
571 public static void setDoJaxpTesting(boolean doJaxpTesting
) {
572 ourDoJaxpTesting
= doJaxpTesting
;
575 public void addMessage(PsiElement context
, String message
, int type
) {
576 if (message
!= null && message
.length() > 0) {
577 if (context
instanceof XmlTag
&& XmlExtension
.getExtension(context
.getContainingFile()).shouldBeHighlightedAsTag((XmlTag
)context
)) {
578 addElementsForTag((XmlTag
)context
, message
, type
== ERROR ? HighlightInfoType
.ERROR
: type
== WARNING ? HighlightInfoType
.WARNING
: HighlightInfoType
.INFO
, null);
581 addToResults(HighlightInfo
.createHighlightInfo(HighlightInfoType
.WRONG_REF
, context
, message
));
586 public void addMessage(final PsiElement context
, final String message
, final ErrorType type
, final IntentionAction
... fixes
) {
587 if (message
!= null && message
.length() > 0) {
588 final PsiFile containingFile
= context
.getContainingFile();
589 final HighlightInfoType defaultInfoType
= type
== ErrorType
.ERROR ? HighlightInfoType
.ERROR
: type
== ErrorType
.WARNING ? HighlightInfoType
.WARNING
: HighlightInfoType
.INFO
;
591 if (context
instanceof XmlTag
&& XmlExtension
.getExtension(containingFile
).shouldBeHighlightedAsTag((XmlTag
)context
)) {
592 addElementsForTagWithManyQuickFixes((XmlTag
)context
, message
, defaultInfoType
, fixes
);
595 final PsiElement contextOfFile
= containingFile
.getContext();
596 final HighlightInfo highlightInfo
;
598 if (contextOfFile
!= null) {
599 final int offsetInRealDocument
= PsiUtilBase
.findInjectedElementOffsetInRealDocument(context
);
600 highlightInfo
= HighlightInfo
.createHighlightInfo(defaultInfoType
, context
.getTextRange().shiftRight(offsetInRealDocument
), message
);
602 highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.WRONG_REF
, context
, message
);
606 for (final IntentionAction quickFixAction
: fixes
) {
607 if (quickFixAction
== null) continue;
608 QuickFixAction
.registerQuickFixAction(highlightInfo
, quickFixAction
);
611 addToResults(highlightInfo
);
616 public static void visitJspElement(OuterLanguageElement text
) {
617 PsiElement parent
= text
.getParent();
619 if (parent
instanceof XmlText
) {
620 parent
= parent
.getParent();
623 parent
.putUserData(DO_NOT_VALIDATE_KEY
, "");
626 public boolean suitableForFile(final PsiFile file
) {
627 return file
instanceof XmlFile
;
630 public void visit(final PsiElement element
, final HighlightInfoHolder holder
) {
631 element
.accept(this);
633 List
<HighlightInfo
> result
= myResult
;
634 holder
.addAll(result
);
638 public boolean analyze(Runnable action
, final boolean updateWholeFile
, final PsiFile file
) {
648 public HighlightVisitor
clone() {
649 return new XmlHighlightVisitor();
656 public static String
getUnquotedValue(XmlAttributeValue value
, XmlTag tag
) {
657 String unquotedValue
= StringUtil
.stripQuotesAroundValue(value
.getText());
659 if (tag
instanceof HtmlTag
) {
660 unquotedValue
= unquotedValue
.toLowerCase();
663 return unquotedValue
;