protected void checkAttribute(
      @NotNull final XmlAttribute attribute,
      @NotNull final ProblemsHolder holder,
      final boolean isOnTheFly) {
    final XmlTag tag = attribute.getParent();

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

      XmlAttributeDescriptor attributeDescriptor =
          elementDescriptor.getAttributeDescriptor(attribute);

      final String name = attribute.getName();

      if (attributeDescriptor == null && !attribute.isNamespaceDeclaration()) {
        if (!XmlUtil.attributeFromTemplateFramework(name, tag)
            && (!isCustomValuesEnabled() || !isCustomValue(name))) {
          final ASTNode node = attribute.getNode();
          assert node != null;
          final PsiElement nameElement =
              XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node).getPsi();

          boolean maySwitchToHtml5 =
              HtmlUtil.isCustomHtml5Attribute(name) && !HtmlUtil.hasNonHtml5Doctype(tag);
          LocalQuickFix[] quickfixes = new LocalQuickFix[maySwitchToHtml5 ? 3 : 2];
          quickfixes[0] =
              new AddCustomTagOrAttributeIntentionAction(
                  getShortName(), name, XmlEntitiesInspection.UNKNOWN_ATTRIBUTE);
          quickfixes[1] = new RemoveAttributeIntentionAction(name);
          if (maySwitchToHtml5) {
            quickfixes[2] = new SwitchToHtml5WithHighPriorityAction();
          }

          holder.registerProblem(
              nameElement,
              XmlErrorMessages.message("attribute.is.not.allowed.here", name),
              ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
              quickfixes);
        }
      }
    }
  }
  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);
    }
  }