protected @NotNull String getNamespaceByPrefix(String prefix) {
    BidirectionalMap<String, String> map = getNamespaceMap(myTag);
    if (map != null) {
      final String ns = map.get(prefix);
      if (ns != null) return ns;
    }
    XmlTag parentTag = myTag.getParentTag();
    if (parentTag != null) return parentTag.getNamespaceByPrefix(prefix);
    //The prefix 'xml' is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace. It MAY, but need not, be declared
    if (XML_NS_PREFIX.equals(prefix)) return XmlUtil.XML_NAMESPACE_URI;

    if (!prefix.isEmpty() &&
        !myTag.hasNamespaceDeclarations() &&
        myTag.getNamespacePrefix().equals(prefix)) {
      // When there is no namespace declarations then qualified names should be just used in dtds
      // myTag implies that we may have "" namespace prefix ! (see last paragraph in Namespaces in Xml, Section 5)

      String result = RecursionManager.doPreventingRecursion(Trinity.create("getNsByPrefix", myTag, prefix), true, () -> {
        final String nsFromEmptyPrefix = myTag.getNamespaceByPrefix("");
        if (nsFromEmptyPrefix.isEmpty()) return nsFromEmptyPrefix;

        final XmlNSDescriptor nsDescriptor = myTag.getNSDescriptor(nsFromEmptyPrefix, false);
        final XmlElementDescriptor descriptor = nsDescriptor != null ? nsDescriptor.getElementDescriptor(myTag) : null;
        final String nameFromRealDescriptor =
          descriptor != null && descriptor.getDeclaration() != null && descriptor.getDeclaration().isPhysical()
          ? descriptor.getName()
          : "";
        if (nameFromRealDescriptor.equals(myTag.getName())) return nsFromEmptyPrefix;
        return XmlUtil.EMPTY_URI;
      });
      if (result != null) {
        return result;
      }
    }
    return XmlUtil.EMPTY_URI;
  }
protected @NotNull String getNamespaceByPrefix(String prefix)