xsl-list
[Top] [All Lists]

Re: pretty-printing XML into HTML

2003-12-18 16:43:33
Dimitre,
Thanks for your responses re: XPV.
Bug fixes to this are not urgent for me; I have a solution that
works. So don't feel pressured to make bug fixes for my sake.

  Thank you for finding the bug(s) related to displaying of
namespace
definitions by the XPV for Mozilla. The fix will be part of the next
release.

I just checked and it is clear that the handling of
namespaces is not a bug,
but was really forced by the fact that the XPath engine used
by Mozilla did
not support the "namespace" axis at all.

Ah. I wasn't aware of that shortcoming in Mozilla. :-S

However there are ways to correct the incorrect output (in this
case at least), even without use of the "namespace"
axis. (There may not be an easy way to do more elegant placement
of namespace declarations, but that's not a big deal.)
Specifically, the findNamespace template should not only
check whether the current element's namespace prefix and uri
have been declared the same way on the nearest ancestor [hmm,
nearest... is that another bug?*], but also whether the current element's
attributes' namespace prefixes and ns-uris have been declared
the same way on the nearest ancestor.

E.g. replace the findNamespace and findAncNamespace templates
with the following:

  <!-- generate namespace declarations as needed for current
        element node and its attributes. -->
  <xsl:template name="findNamespace">
    <xsl:variable name="curnode" select="." />

    <xsl:for-each select=".|@*">
      <xsl:variable name="vName" select="substring-before(name(), ':')"/>
      <xsl:variable name="vUri" select="namespace-uri(.)"/>

      <xsl:variable name="vAncestNamespace">
        <xsl:call-template name="findAncNamespace">
          <xsl:with-param name="pName" select="$vName"/>
          <xsl:with-param name="pUri" select="$vUri"/>
          <xsl:with-param name="pNode" select="$curnode" />
        </xsl:call-template>
      </xsl:variable>

      <xsl:if test="not(number($vAncestNamespace))">
        <!-- not sure if "parent::*" should be "$curnode/parent::*" -->
        <xsl:if test="parent::* or namespace-uri() or contains(name(), ':')">
          <xsl:text> </xsl:text>
          <span class="namespace">
            <xsl:value-of select="'xmlns'"/>
            <xsl:if test="contains(name(), ':')">
              <xsl:value-of select="concat(':', $vName)"/>
            </xsl:if>
          </span>
          <span class="markup">="</span>
          <span class="namespace">
            <xsl:value-of select="namespace-uri()"/>
          </span>
          <span class="markup">"</span>
        </xsl:if>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <!-- Return 1 if an ancestor of pNode, or an attribute thereof, has the same 
prefix (pName) and
namespace-uri (pUri) as
      the current node. Return 0 otherwise. -->
  <xsl:template name="findAncNamespace">
    <xsl:param name="pNode" select="."/>
    <xsl:param name="pName" select="substring-before(name(), ':')"/>
    <xsl:param name="pUri" select="namespace-uri(.)"/>

     <xsl:choose>
      <xsl:when test="not($pNode/parent::*)">0</xsl:when>
      <xsl:otherwise>
        <xsl:choose>
          <xsl:when test="not(($pName = substring-before(name($pNode/..), ':')
                                            and $pUri  = 
namespace-uri($pNode/..))
                          or $pNode/../@*[$pName = substring-before(name(), ':')
                                                      and $pUri = 
namespace-uri()]
                            )">
            <xsl:call-template name="findAncNamespace">
              <xsl:with-param name="pNode" select="$pNode/.."/>
              <xsl:with-param name="pName" select="$pName"/>
              <xsl:with-param name="pUri" select="$pUri"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>1</xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>

As it was impossible to have the nodeset of all namespace
nodes belonging to
a given element, it was impossible to deal with namespaces as such.
Therefore, I could only check simple things as if a name had
a namespace
prefix or namespace-uri().


I agree that if Mozilla's XPath implementation was a fully
conformant one
then this would be a bug that should be fixed immediately.
However, this is
impossible without the lack of support for the "namespace" axis.

While this particular bug can be fixed, as shown above,
I agree it isn't always possible to declare all needed namespaces.
E.g. if your XML happens to be an XSL stylesheet, and some namespace
declarations are used only by XPath expressions (which are of
course in attribute values rather than in element or attribute names),
I don't think there's any way to figure out what the namespace URI's
should be without the namespace:: axis.

I have not looked for newer versions of Mozilla -- if and
when they have
support for the "namespace" axis, XPV for mozilla will start handling
namespace nodes correctly for the first time.

FWIW, I just checked Mozilla 1.5 and the namespace axis still
doesn't seem to work.  :-(

Lars

*P.S. There is another bug:
findAncNamespace needs to be fixed so that besides
checking whether any ancestor has prefix $pName and ns-uri
$pUri, it needs to make sure there isn't a closer ancestor that
has prefix $pName and a different ns-uri.

For example, the input XML
  <p1:anc1 xmlns:p1="ns1">
    <p1:anc2 xmlns:p1="ns2">
      <p1:anc3 xmlns:p1="ns1" />
    </p1:anc2>
  </p1:anc1>
gets displayed incorrectly as
- <p1:anc1 xmlns:p1="ns1">
  - <p1:anc2 xmlns:p1="ns2">
      <p1:anc3/>
    </p1:anc2>
  </p1:anc1>

i.e. the anc3 element is in the wrong namespace.

The fixed findAncNamespace:

  <!-- Return 1 if an ancestor of pNode (or an attribute thereof) has the same 
prefix (pName) and
namespace-uri (pUri) as
      the current node, and there is no closer ancestor (or attribute
        thereof) that has the same prefix but a different namespace-uri. Return 
0 otherwise. -->
  <xsl:template name="findAncNamespace">
    <xsl:param name="pNode" select="."/>
    <xsl:param name="pName" select="substring-before(name(), ':')"/>
    <xsl:param name="pUri" select="namespace-uri(.)"/>

     <xsl:choose>
      <xsl:when test="not($pNode/parent::*)">0</xsl:when>
      <xsl:otherwise>
        <xsl:choose>
          <xsl:when test="($pNode/.. | $pNode/../@*)
                                      [$pName = substring-before(name(), ':') 
and $pUri =
namespace-uri()]">
            1
          </xsl:when>
          <xsl:when test="($pNode/.. | $pNode/../@*)
                                      [$pName = substring-before(name(), 
':')]"> <!-- and $pUri !=
namespace-uri() -->
            0
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="findAncNamespace">
              <xsl:with-param name="pNode" select="$pNode/.."/>
              <xsl:with-param name="pName" select="$pName"/>
              <xsl:with-param name="pUri" select="$pUri"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list