Default attribute manifestation in Schematron Quick Fixes

This should cover W3C XML Schema, Relax NG and DTD related problems.
HomeGoods
Posts: 87

Default attribute manifestation in Schematron Quick Fixes

Sat Jun 27, 2015 1:49 am

Validate this DITA topic body on oXygen:

Code: Select all

   <body>
      <section>
         <title>test</title>
         <title><b>test</b></title>
      </section>
   </body>

[oxygen]\frameworks\dita\resources\dita-1.2-for-xslt2-other.sch will report the warning "section should only contain one title element" and propose the quick fix to "Convert other titles to text".
If you apply the fix, the elements now look like this:

Code: Select all

   <body>
      <section>
         <title>test</title>
         <b class="+ topic/ph hi-d/b ">test</b>
      </section>
   </body>

@class manifests in <b>. It may be technically fine... but still annoying. Is it possible to suppress its manifestation?
I'm writing a similar SQF and looking for how to avoid this behavior.
Radu
Posts: 5758

Re: Default attribute manifestation in Schematron Quick Fixe

Mon Jun 29, 2015 2:58 pm

Hi,

The quick fix which gets applied in that case looks like:

Code: Select all

<sqf:replace match="*[contains(@class, ' topic/title ')][position() > 1]" select="child::node()"/>


so it serializes the child content nodes of the second title.
But the XSLT stylesheet template which gets applied on the XML to produce the replacement content will receive the original XML with all default attributes (class for example) expanded by default so they will also be inserted in the actual content when the quick fix is applied.

I will add an issue on our side to find a solution for this.
In the meantime a slightly complicated workaround would be this one:

1) In the Schematron file on the root element declare the XSLT namespace mapping:

Code: Select all

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"


2) On the first level inside the Schematron root element add this XSLT template:

Code: Select all

    <xsl:template match="node() | @*" mode="noClass">
        <xsl:copy>
            <xsl:apply-templates select="@* except @class" mode="noClass"/>
            <xsl:apply-templates select="node()" mode="noClass"/>
        </xsl:copy>
    </xsl:template>


3) In the pattern which matches multiple section titles add a variable which calls that template to serialize the content and call that variable from the quick fix like:

Code: Select all

<pattern id="multiple_section_titles">
...................
            <sqf:fix id="convertTitles">
                <sqf:description>
                    <sqf:title>Convert other titles to text</sqf:title>
                </sqf:description>
                <xsl:variable name="serialized">
                    <xsl:apply-templates select="*[contains(@class, ' topic/title ')][position() > 1]/child::node()" mode="noClass"/>
                </xsl:variable>
                <sqf:replace match="*[contains(@class, ' topic/title ')][position() > 1]" select="$serialized"/>
            </sqf:fix>
................


Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
HomeGoods
Posts: 87

Re: Default attribute manifestation in Schematron Quick Fixe

Mon Jun 29, 2015 4:54 pm

It worked. Ideally I'd prefer something global like Saxon -expand option. But hardcoding to remove @class will do in my current use case. Thank you for the practical workaround.
Radu
Posts: 5758

Re: Default attribute manifestation in Schematron Quick Fixe

Tue Jun 30, 2015 8:56 am

Hi,

The problem with setting an -expand option to the processor would be that your Schematron would no longer be able to properly match in its rules and asserts DITA elements by their class values.

So in a way we need default attribute expansion because we want to properly match DITA element no matter what specialization they have but when serializing XML nodes back to content in a quick fix we want to avoid serializing certain attributes.

One possibility I was thinking of was something would have been to support something like this:

Code: Select all

<sqf:replace match="*[contains(@class, ' topic/title ')][position() > 1]" select="child::node()" ignore-attributes="class"/>


which if supported would instruct the XSLT templates created from the quick fix not to serialize the @class attribute.

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Patrik
Posts: 228
Location: Hamburg/Germany

Re: Default attribute manifestation in Schematron Quick Fixe

Tue Jun 30, 2015 10:14 am

Hi,

an alternative might be not to expand the default attributes but access them from the schema. I implement a function for that:

Code: Select all

<xsl:function name="my:GetAttributeDefaultValue" as="xs:string?">
   <xsl:param name="Element"       as="element()"/>
   <xsl:param name="AttributeName" as="xs:string"/>
   
   <xsl:variable name="TypeFct"         as="function(*)"  select="saxon:type($Element)"/>
   <xsl:variable name="AttributeUseFct" as="function(*)?" select="$TypeFct('attribute uses')[.('attribute declaration')('name') = $AttributeName]"/>
   <xsl:if test="exists($AttributeUseFct)">
      <xsl:variable name="UseDefaultFct" as="function(*)?"     select="$AttributeUseFct('value constraint')[.('variety') = ('fixed', 'default')]"/>
      <xsl:choose>
         <xsl:when test="exists($UseDefaultFct)">
            <xsl:value-of select="$UseDefaultFct('value')"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:variable name="AttributeDeclFct"   as="function(*)?" select="$AttributeUseFct('attribute declaration')"/>
            <xsl:variable name="DeclDefaultFct"     as="function(*)?" select="$AttributeDeclFct('value constraint')[.('variety') = ('fixed', 'default')]"/>
            <xsl:if test="exists($DeclDefaultFct)">
               <xsl:value-of select="$DeclDefaultFct('value')"/>
            </xsl:if>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:if>
</xsl:function>


This function works only with saxon and might be limited to xsd. Also I didn't test that from within schematron/sqf, yet.

Reragds,
Patrik
HomeGoods
Posts: 87

Re: Default attribute manifestation in Schematron Quick Fixe

Wed Jul 01, 2015 11:24 pm

An ingenious function. I like it.
Unfortunately enabling XSLT3 in the skeleton and switching from DTD to XSD would be overkill for me at the moment...
Patrik
Posts: 228
Location: Hamburg/Germany

Re: Default attribute manifestation in Schematron Quick Fixe

Fri Jul 03, 2015 8:07 am

There's yet another approach that I used successfully and to keep default attributes from the output and that works with any schema (dtd, xsd or rng):
  • Load the input document without expanding default attributes into a seperate variable.
  • Create an xpath expression to the node you want to process.
  • Apply this xpath to the variable from step 1.
  • Use that result for as input rather than the current one.

To load a document without default attributes you have to call java methods from xslt (works with saxon and xslt 2.0):

Code: Select all

<xsl:variable
   name        = "documentWithoutDefaultAttributes"
   as          = "document-node()"
   xmlns:jdb   = "java:javax.xml.parsers.DocumentBuilder"
   xmlns:jdbf  = "java:javax.xml.parsers.DocumentBuilderFactory">
   
   <xsl:variable
      name    = "javaDocBuilderFactory"
      as      = "jt:javax.xml.parsers.DocumentBuilderFactory"
      select  = "jdbf:newInstance()"/>
   <xsl:variable
      name    = "javaDocBuilder"
      as      = "jt:javax.xml.parsers.DocumentBuilder"
      select  = "jdbf:newDocumentBuilder($javaDocBuilderFactory)"/>
   
   <xsl:sequence select="jdb:parse($javaDocBuilder, base-uri())"/>
</xsl:variable>


Regards,
Patrik
HomeGoods
Posts: 87

Re: Default attribute manifestation in Schematron Quick Fixe

Fri Jul 03, 2015 3:16 pm

Interesting approach. After adding a few more jdbf:setFeature() to disable loading DTD, it worked. Thanks for sharing it.
The catch is that the document at base-uri() must be in sync with the in-memory one.

Return to “XML Schemas”

Who is online

Users browsing this forum: No registered users and 1 guest