ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / xml / impl / src / com / intellij / codeInsight / daemon / impl / analysis / XmlHighlightVisitor.java
blobc02a672d756bfe156adcffdae971b866c6593650
1 /*
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;
64 import java.util.Set;
65 import java.util.StringTokenizer;
67 /**
68 * @author Mike
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() {
79 return false;
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);
109 } else {
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 ||
132 descriptor == null
135 return;
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,
153 final int offset,
154 int length,
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(
162 warning,
163 childByRole, startOffset, startOffset + length,
164 localizedMessage, HighlightInfo.htmlEscapeToolTip(localizedMessage)
167 if (highlightInfo == null) {
168 highlightInfo = HighlightInfo.createHighlightInfo(
169 warning,
170 new TextRange(startOffset, startOffset + length),
171 localizedMessage,
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)*/) {
206 return;
210 addElementsForTag(
211 tag,
212 XmlErrorMessages.message("element.is.not.allowed.here", name),
213 getTagProblemInfoType(tag),
214 null
216 return;
219 if (elementDescriptor instanceof AnyXmlElementDescriptor ||
220 elementDescriptor == null
222 elementDescriptor = tag.getDescriptor();
225 if (elementDescriptor == null) return;
227 else {
228 //root tag
229 elementDescriptor = tag.getDescriptor();
231 if (elementDescriptor == null) {
232 addElementsForTag(tag, XmlErrorMessages.message("element.must.be.declared", name), HighlightInfoType.WRONG_REF, null);
233 return;
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();
262 reportOneTagProblem(
263 tag,
264 attrName,
265 localizedMessage,
266 insertRequiredAttributeIntention,
267 HighlightDisplayKey.find(RequiredAttributesInspection.SHORT_NAME),
268 inspection,
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,
283 final String name,
284 final String localizedMessage,
285 final IntentionAction basicIntention,
286 final HighlightDisplayKey key,
287 final XmlEntitiesInspection inspection,
288 final int type) {
289 boolean htmlTag = false;
291 if (tag instanceof HtmlTag) {
292 htmlTag = true;
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(
300 tag,
301 localizedMessage,
302 isInjectedHtmlTagForWhichNoProblemsReporting((HtmlTag)tag) ?
303 HighlightInfoType.INFORMATION :
304 SeverityRegistrar.getInstance(tag.getProject()).getHighlightInfoTypeBySeverity(profile.getErrorLevel(key, tag).getSeverity()),
305 intentionAction,
306 basicIntention);
307 } else if (!htmlTag) {
308 addElementsForTag(
309 tag,
310 localizedMessage,
311 HighlightInfoType.ERROR,
312 basicIntention
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())) {
324 return true;
328 return false;
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;
342 return false;
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());
360 return;
362 final String namespace = attribute.getNamespace();
364 if (XmlUtil.XML_SCHEMA_INSTANCE_URI.equals(namespace)) {
365 checkReferences(attribute.getValueElement());
366 return;
369 XmlElementDescriptor elementDescriptor = tag.getDescriptor();
370 if (elementDescriptor == null ||
371 elementDescriptor instanceof AnyXmlElementDescriptor ||
372 ourDoJaxpTesting) {
373 return;
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);
392 else {
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);
409 @Nullable
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);
421 assert node != null;
422 final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node);
423 assert child != null;
424 final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(
425 tagProblemInfoType, child,
426 localizedMessage
428 addToResults(highlightInfo);
430 QuickFixAction.registerQuickFixAction(highlightInfo, removeAttributeIntention);
432 return highlightInfo;
435 return null;
438 private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) {
439 if (skipValidation(tag)) {
440 return;
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);
485 return;
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());
498 if (error != null) {
499 addToResults(HighlightInfo.createHighlightInfo(
500 getTagProblemInfoType(tag),
501 value,
502 error));
503 return;
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) {
525 continue;
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(),
544 description
546 addToResults(info);
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) {
555 String message;
556 if (reference instanceof EmptyResolveMessageProvider) {
557 message = ((EmptyResolveMessageProvider)reference).getUnresolvedMessagePattern();
559 else {
560 //noinspection UnresolvedPropertyKey
561 message = PsiBundle.message("cannot.resolve.symbol");
564 String description;
565 try {
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));
572 return description;
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>();
589 myResult.add(info);
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);
601 else {
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);
615 else {
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);
622 } else {
623 highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message);
626 if (fixes != null) {
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);
656 myResult = null;
659 public boolean analyze(Runnable action, final boolean updateWholeFile, final PsiFile file) {
660 try {
661 action.run();
663 finally {
664 myResult = null;
666 return true;
669 public HighlightVisitor clone() {
670 return new XmlHighlightVisitor();
673 public int order() {
674 return 1;
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;