private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) {
    if (skipValidation(tag)) {
      return;
    }

    final XmlAttribute[] attributes = tag.getAttributes();
    final PsiFile containingFile = tag.getContainingFile();
    final XmlExtension extension =
        containingFile instanceof XmlFile
            ? XmlExtension.getExtension(containingFile)
            : XmlExtension.DEFAULT_EXTENSION;
    for (XmlAttribute tagAttribute : attributes) {
      ProgressManager.checkCanceled();
      if (attribute != tagAttribute
          && Comparing.strEqual(attribute.getName(), tagAttribute.getName())) {
        final String localName = attribute.getLocalName();

        if (extension.canBeDuplicated(tagAttribute))
          continue; // multiple import attributes are allowed in jsp directive

        HighlightInfo highlightInfo =
            HighlightInfo.createHighlightInfo(
                getTagProblemInfoType(tag),
                XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(
                    SourceTreeToPsiMap.psiElementToTree(attribute)),
                XmlErrorMessages.message("duplicate.attribute", localName));
        addToResults(highlightInfo);

        IntentionAction intentionAction = new RemoveAttributeIntentionFix(localName, attribute);

        QuickFixAction.registerQuickFixAction(highlightInfo, intentionAction);
      }
    }
  }
  @Override
  public void visitXmlAttributeValue(XmlAttributeValue value) {
    final PsiElement parent = value.getParent();
    if (!(parent instanceof XmlAttribute)) {
      checkReferences(value);
      return;
    }

    XmlAttribute attribute = (XmlAttribute) parent;

    XmlTag tag = attribute.getParent();

    XmlElementDescriptor elementDescriptor = tag.getDescriptor();
    XmlAttributeDescriptor attributeDescriptor =
        elementDescriptor != null ? elementDescriptor.getAttributeDescriptor(attribute) : null;

    if (attributeDescriptor != null && !skipValidation(value)) {
      String error = attributeDescriptor.validateValue(value, attribute.getValue());

      if (error != null) {
        addToResults(HighlightInfo.createHighlightInfo(getTagProblemInfoType(tag), value, error));
        return;
      }
    }

    checkReferences(value);
  }
  @Nullable
  private HighlightInfo reportAttributeProblem(
      final XmlTag tag,
      final String localName,
      final XmlAttribute attribute,
      final String localizedMessage) {

    final RemoveAttributeIntentionFix removeAttributeIntention =
        new RemoveAttributeIntentionFix(localName, attribute);

    if (!(tag instanceof HtmlTag)) {
      final HighlightInfoType tagProblemInfoType = HighlightInfoType.WRONG_REF;

      final ASTNode node = SourceTreeToPsiMap.psiElementToTree(attribute);
      assert node != null;
      final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node);
      assert child != null;
      final HighlightInfo highlightInfo =
          HighlightInfo.createHighlightInfo(tagProblemInfoType, child, localizedMessage);
      addToResults(highlightInfo);

      QuickFixAction.registerQuickFixAction(highlightInfo, removeAttributeIntention);

      return highlightInfo;
    }

    return null;
  }
  @Override
  public void visitXmlProcessingInstruction(XmlProcessingInstruction processingInstruction) {
    super.visitXmlProcessingInstruction(processingInstruction);
    PsiElement parent = processingInstruction.getParent();

    if (parent instanceof XmlProlog && processingInstruction.getText().startsWith("<?xml")) {
      for (PsiElement e = PsiTreeUtil.prevLeaf(processingInstruction);
          e != null;
          e = PsiTreeUtil.prevLeaf(e)) {
        if (e instanceof PsiWhiteSpace && PsiTreeUtil.prevLeaf(e) != null
            || e instanceof OuterLanguageElement) {
          continue;
        }
        PsiElement eParent = e.getParent();
        if (eParent instanceof PsiComment) e = eParent;
        if (eParent instanceof XmlProcessingInstruction) break;

        addToResults(
            HighlightInfo.createHighlightInfo(
                HighlightInfoType.ERROR,
                e,
                XmlErrorMessages.message("xml.declaration.should.precede.all.document.content")));
      }
    }
  }
  private void doCheckRefs(final PsiElement value, final PsiReference[] references, int start) {
    for (int i = start; i < references.length; ++i) {
      PsiReference reference = references[i];
      ProgressManager.checkCanceled();
      if (reference == null) {
        continue;
      }
      if (!hasBadResolve(reference, false)) {
        continue;
      }
      String description = getErrorDescription(reference);

      final int startOffset = reference.getElement().getTextRange().getStartOffset();
      final TextRange referenceRange = reference.getRangeInElement();

      // logging for IDEADEV-29655
      if (referenceRange.getStartOffset() > referenceRange.getEndOffset()) {
        LOG.error(
            "Reference range start offset > end offset:  "
                + reference
                + ", start offset: "
                + referenceRange.getStartOffset()
                + ", end offset: "
                + referenceRange.getEndOffset());
      }

      HighlightInfoType type =
          getTagProblemInfoType(PsiTreeUtil.getParentOfType(value, XmlTag.class));
      if (value instanceof XmlAttributeValue) {
        PsiElement parent = value.getParent();
        if (parent instanceof XmlAttribute) {
          String name = ((XmlAttribute) parent).getName().toLowerCase();
          if (type.getSeverity(null).compareTo(HighlightInfoType.WARNING.getSeverity(null)) > 0
              && name.endsWith("stylename")) {
            type = HighlightInfoType.WARNING;
          } else if (name.equals("href")
              && type.getSeverity(null) == HighlightInfoType.WARNING.getSeverity(null)) {
            continue;
          }
        }
      }
      HighlightInfo info =
          HighlightInfo.createHighlightInfo(
              type,
              startOffset + referenceRange.getStartOffset(),
              startOffset + referenceRange.getEndOffset(),
              description);
      addToResults(info);
      if (reference instanceof QuickFixProvider)
        ((QuickFixProvider) reference).registerQuickfix(info, reference);
      UnresolvedReferenceQuickFixProvider.registerReferenceFixes(
          reference, new QuickFixActionRegistrarImpl(info));
    }
  }
  public void addMessage(
      final PsiElement context,
      final String message,
      final ErrorType type,
      final IntentionAction... fixes) {
    if (message != null && message.length() > 0) {
      final PsiFile containingFile = context.getContainingFile();
      final HighlightInfoType defaultInfoType =
          type == ErrorType.ERROR
              ? HighlightInfoType.ERROR
              : type == ErrorType.WARNING
                  ? HighlightInfoType.WARNING
                  : HighlightInfoType.WEAK_WARNING;

      if (context instanceof XmlTag
          && XmlExtension.getExtension(containingFile).shouldBeHighlightedAsTag((XmlTag) context)) {
        addElementsForTagWithManyQuickFixes((XmlTag) context, message, defaultInfoType, fixes);
      } else {
        final PsiElement contextOfFile = containingFile.getContext();
        final HighlightInfo highlightInfo;

        if (contextOfFile != null) {
          TextRange range =
              InjectedLanguageManager.getInstance(context.getProject())
                  .injectedToHost(context, context.getTextRange());
          highlightInfo = HighlightInfo.createHighlightInfo(defaultInfoType, range, message);
        } else {
          highlightInfo =
              HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message);
        }

        if (fixes != null) {
          for (final IntentionAction quickFixAction : fixes) {
            if (quickFixAction == null) continue;
            QuickFixAction.registerQuickFixAction(highlightInfo, quickFixAction);
          }
        }
        addToResults(highlightInfo);
      }
    }
  }
  private void bindMessageToAstNode(
      final PsiElement childByRole,
      final HighlightInfoType warning,
      final int offset,
      int length,
      final String localizedMessage,
      IntentionAction... quickFixActions) {
    if (childByRole != null) {
      final TextRange textRange = childByRole.getTextRange();
      if (length == -1) length = textRange.getLength();
      final int startOffset = textRange.getStartOffset() + offset;

      HighlightInfo highlightInfo =
          HighlightInfo.createHighlightInfo(
              warning,
              childByRole,
              startOffset,
              startOffset + length,
              localizedMessage,
              HighlightInfo.htmlEscapeToolTip(localizedMessage));

      if (highlightInfo == null) {
        highlightInfo =
            HighlightInfo.createHighlightInfo(
                warning,
                new TextRange(startOffset, startOffset + length),
                localizedMessage,
                localizedMessage,
                NONEMPTY_TEXT_ATTRIBUTES);
      }

      for (final IntentionAction quickFixAction : quickFixActions) {
        if (quickFixAction == null) continue;
        QuickFixAction.registerQuickFixAction(highlightInfo, textRange, quickFixAction, null);
      }
      addToResults(highlightInfo);
    }
  }
  @Override
  public void visitXmlToken(XmlToken token) {
    IElementType tokenType = token.getTokenType();
    if (tokenType == XmlTokenType.XML_NAME || tokenType == XmlTokenType.XML_TAG_NAME) {
      PsiElement element = token.getPrevSibling();
      while (element instanceof PsiWhiteSpace) element = element.getPrevSibling();

      if (element instanceof XmlToken) {
        if (((XmlToken) element).getTokenType() == XmlTokenType.XML_START_TAG_START) {
          PsiElement parent = element.getParent();

          if (parent instanceof XmlTag
              && !(token.getNextSibling() instanceof OuterLanguageElement)) {
            checkTag((XmlTag) parent);
          }
        }
      } else {
        PsiElement parent = token.getParent();

        if (parent instanceof XmlAttribute
            && !(token.getNextSibling() instanceof OuterLanguageElement)) {
          checkAttribute((XmlAttribute) parent);
        }
      }
    } else if (tokenType == XmlTokenType.XML_DATA_CHARACTERS
        && token.getParent() instanceof XmlText) {
      if (token.textContains(']') && token.textContains('>')) {
        String s = token.getText();
        String marker = "]]>";
        int i = s.indexOf(marker);

        if (i != -1) { // TODO: fix
          TextRange textRange = token.getTextRange();
          int start = textRange.getStartOffset() + i;
          HighlightInfoType type =
              PsiTreeUtil.getParentOfType(token, XmlTag.class) instanceof HtmlTag
                  ? HighlightInfoType.WARNING
                  : HighlightInfoType.ERROR;
          HighlightInfo info =
              HighlightInfo.createHighlightInfo(
                  type,
                  start,
                  start + marker.length(),
                  XmlErrorMessages.message(
                      "cdata.end.should.not.appear.in.content.unless.to.mark.end.of.cdata.section"));
          addToResults(info);
        }
      }
    }
  }
 public void addMessage(PsiElement context, String message, int type) {
   if (message != null && message.length() > 0) {
     if (context instanceof XmlTag
         && XmlExtension.getExtension(context.getContainingFile())
             .shouldBeHighlightedAsTag((XmlTag) context)) {
       HighlightInfoType infoType =
           type == ERROR
               ? HighlightInfoType.ERROR
               : type == WARNING ? HighlightInfoType.WARNING : HighlightInfoType.WEAK_WARNING;
       addElementsForTag((XmlTag) context, message, infoType, null);
     } else {
       addToResults(
           HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message));
     }
   }
 }
  private void checkAttribute(XmlAttribute attribute) {
    XmlTag tag = attribute.getParent();

    final String name = attribute.getName();
    PsiElement prevLeaf = PsiTreeUtil.prevLeaf(attribute);

    if (!(prevLeaf instanceof PsiWhiteSpace)) {
      TextRange textRange = attribute.getTextRange();
      addToResults(
          HighlightInfo.createHighlightInfo(
              tag instanceof HtmlTag ? HighlightInfoType.WARNING : HighlightInfoType.ERROR,
              textRange.getStartOffset(),
              textRange.getStartOffset(),
              XmlErrorMessages.message("attribute.should.be.preceded.with.space")));
    }

    if (attribute.isNamespaceDeclaration()) {
      checkReferences(attribute.getValueElement());
      return;
    }
    final String namespace = attribute.getNamespace();

    if (XmlUtil.XML_SCHEMA_INSTANCE_URI.equals(namespace)) {
      checkReferences(attribute.getValueElement());
      return;
    }

    XmlElementDescriptor elementDescriptor = tag.getDescriptor();
    if (elementDescriptor == null
        || elementDescriptor instanceof AnyXmlElementDescriptor
        || ourDoJaxpTesting) {
      return;
    }

    XmlAttributeDescriptor attributeDescriptor =
        elementDescriptor.getAttributeDescriptor(attribute);

    if (attributeDescriptor == null) {
      if (!XmlUtil.attributeFromTemplateFramework(name, tag)) {
        final String localizedMessage =
            XmlErrorMessages.message("attribute.is.not.allowed.here", name);
        final HighlightInfo highlightInfo =
            reportAttributeProblem(tag, name, attribute, localizedMessage);
        if (highlightInfo != null) {
          final XmlFile xmlFile = (XmlFile) tag.getContainingFile();
          if (xmlFile != null) {
            XmlExtension.getExtension(xmlFile).createAddAttributeFix(attribute, highlightInfo);
          }
        }
      }
    } else {
      checkDuplicateAttribute(tag, attribute);

      if (tag instanceof HtmlTag
          && attribute.getValueElement() == null
          && !HtmlUtil.isSingleHtmlAttribute(name)) {
        final String localizedMessage =
            XmlErrorMessages.message("empty.attribute.is.not.allowed", name);
        reportAttributeProblem(tag, name, attribute, localizedMessage);
      }

      // we skip resolve of attribute references since there is separate check when taking attribute
      // descriptors
      PsiReference[] attrRefs = attribute.getReferences();
      doCheckRefs(attribute, attrRefs, attribute.getNamespacePrefix().length() > 0 ? 2 : 1);
    }
  }