private static CachedValueProvider.Result<XmlEntityDecl> doResolveEntity(final PsiElement targetElement,
                                                                           final String entityName,
                                                                           final PsiFile contextFile) {
    return RecursionManager.doPreventingRecursion(targetElement, true, new Computable<>() {
      @Override
      public CachedValueProvider.Result<XmlEntityDecl> compute() {
        final List<PsiElement> deps = new ArrayList<>();
        final XmlEntityDecl[] result = {null};

        PsiElementProcessor<PsiElement> processor = new PsiElementProcessor<>() {
          @Override
          public boolean execute(@NotNull PsiElement element) {
            if (element instanceof XmlDoctype xmlDoctype) {
              final String dtdUri = getDtdForEntity(xmlDoctype);
              if (dtdUri != null) {
                XmlFile file = XmlUtil.getContainingFile(element);
                if (file == null) return true;
                final XmlFile xmlFile = XmlUtil.findNamespace(file, dtdUri);
                if (xmlFile != null) {
                  if (xmlFile != targetElement) {
                    deps.add(xmlFile);
                    if (!XmlUtil.processXmlElements(xmlFile, this, true)) return false;
                  }
                }
              }
              final XmlMarkupDecl markupDecl = xmlDoctype.getMarkupDecl();
              if (markupDecl != null) {
                if (!XmlUtil.processXmlElements(markupDecl, this, true)) return false;
              }
            }
            else if (element instanceof XmlEntityDecl entityDecl) {
              final String declName = entityDecl.getName();
              if (StringUtil.equals(declName, entityName)) {
                result[0] = entityDecl;
                deps.add(entityDecl.getContainingFile());
                return false;
              }
            }

            return true;
          }
        };
        FileViewProvider provider = targetElement.getContainingFile().getViewProvider();
        deps.add(provider.getPsi(provider.getBaseLanguage()));

        boolean notfound = PsiTreeUtil.processElements(targetElement, processor);
        if (notfound) {
          if (contextFile != targetElement && contextFile != null && contextFile.isValid()) {
            notfound = PsiTreeUtil.processElements(contextFile, processor);
          }
        }

        if (notfound &&       // no dtd ref at all
            targetElement instanceof XmlFile &&
            deps.size() == 1 &&
            ((XmlFile)targetElement).getFileType() != DTDFileType.INSTANCE
        ) {
          for (XmlFile descriptorFile : XmlExtension.getExtension((XmlFile)targetElement).getCharEntitiesDTDs((XmlFile)targetElement)) {
            if (!descriptorFile.getName().equals(((XmlFile)targetElement).getName() + ".dtd")) {
              deps.add(descriptorFile);
              if (!XmlUtil.processXmlElements(descriptorFile, processor, true)) {
                break;
              }
            }
          }
        }
        return new CachedValueProvider.Result<>(result[0], ArrayUtil.toObjectArray(deps));
      }
    });
  }
private static CachedValueProvider.Result<XmlEntityDecl> doResolveEntity(final PsiElement targetElement,
                                                                           final String entityName,
                                                                           final PsiFile contextFile)