[xsl] Re: Querying Result Tree

Subject: [xsl] Re: Querying Result Tree
From: David Carver <d_a_carver@xxxxxxxxx>
Date: Thu, 22 Dec 2005 10:45:58 -0500
What I want to do is make sure that I don't add a duplicate node to the result document, when an earlier template may have already added the result.

The word "earlier" gives away that you're thinking procedurally. XSLT is a
functional language, the output has to be expressed as a function of the
input, not as a function of things that happened "earlier", because there is
no defined order of execution.

If you can't express the logic as a function of the original input, then use
a multi-phase transformation.


Michael,

Here is what I'm trying to do. I have a W3C Schemas that has multiple includes and imports. What I would like to do is take a given Schemas, and include in an output schemas all of the components that used by that schemas in a flattened schemas. The only included items should be those that are used.

This is what the Schemas could look like:

Schemas 1
    Includes complexTypes and elements from Schemas 2
           Includes complexTypes and elements from Schmeas 3
           Includes complexTypes and elements from schemas 4

I've got it pretty much working, the only big issue I'm having is that some of the ComplexTypes tend to re-occur, and I only want to include them if they haven't already occured. The issue happens when they are nested in an unlimited fashion.

ComplexType 1 may include ComplexType2 which may include ComplexType 3 which may include ComplexType1 again.

Yeah, I know horrible and ugly, but until we can get it redesigned, it's what I'm dealing with. What eventually happens is that the various XSLT processors fail because of too many applys. It basically gets in an infinete loop.


XSLT:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema";>
<xsl:param name="fieldsLocation">file:///D:/Work/OAGIS90POC/Development/STAR/Rev/Resources/Components/Common/NewFields90.xsd</xsl:param>
<xsl:param name="metaLocation">file:///D:/Work/OaGIS90POC/Development/STAR/Rev/Resources/Components/Common/Meta.xsd</xsl:param>
<xsl:param name="componentsLocation">file:///D:/Work/OAGIS90POC/Development/STAR/Rev/Resources/Components/Common/NewComponents90.xsd</xsl:param>
<xsl:variable name="nounLocation">file:///D:/Work/OAGIS90POC/Development/STAR/Rev/Resources/Noun90/RepairOrder.xsd</xsl:variable>
<!-- Process the Root -->
<xsl:template match="xsd:schema">
<xsl:variable name="resultDoc">
<xsl:variable name="currentDoc" select="xsd:element | xsd:complexType | document($nounLocation)/xsd:schema/xsd:element | document($nounLocation)/xsd:schema/xsd:complexType"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsd:import namespace="http://www.starstandards.org/STAR/5/unqualifieddatatypes/1.0"; schemaLocation="../../Resources/Components/Common/UnqualifiedDataTypes.xsd"/>
<xsd:import namespace="http://www.starstandards.org/STAR/5/qualifieddatatypes/1.0"; schemaLocation="../../Resources/Components/Common/QualifiedDataTypes.xsd"/>
<xsd:import namespace="http://www.starstandards.org/STAR/5/codelists"; schemaLocation="../../Resources/Components/Common/CodeLists.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/qualifieddatatypes/1.1"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/QualifiedDataTypes.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/unqualifieddatatypes/1.1"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/UnqualifiedDataTypes.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/codelists"; schemaLocation="../../Resources/Components/OAGIS/Common/CodeLists.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/currencycode/54217:2001"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/CodeList_CurrencyCode_ISO_7_04.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/languagecode/5639:1988"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/CodeList_LanguageCode_ISO_7_04.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/IANAMIMEMediaTypes:2003"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/CodeList_MIMEMediaTypeCode_IANA_7_04.xsd"/>
<xsd:import namespace="http://www.openapplications.org/oagis/9/unitcode/66411:2001"; schemaLocation="../../Resources/Components/OAGIS/CoreComponents/CodeList_UnitCode_UNECE_7_04.xsd"/>
<xsl:copy-of select="xsd:element"/>
<!-- Process the main schemas elements and any of the result tree elements that have been created -->
<xsl:apply-templates select="xsd:element | xsd:complexType | $currentDoc//xsd:element[@ref != '']"/>
</xsl:copy>
</xsl:variable>
<!-- clean up the final result befor outputting -->
<xsl:apply-templates select="$resultDoc/xsd:schema" mode="RemoveDups"/> </xsl:template>


<xsl:template match="xsd:schema" mode="RemoveDups">
<xsl:variable name="elementsSorted">
<xsl:for-each select="xsd:element">
<xsl:sort select="@name"/>
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="complexTypesSorted">
<xsl:for-each select="xsd:complexType">
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:for-each>
</xsl:variable>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="xsd:include | xsd:import">
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:for-each>
<xsl:for-each select="$elementsSorted/xsd:element[not(following-sibling::*/@name = @name)]">
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:for-each>
<xsl:for-each select="$complexTypesSorted/xsd:complexType[not(following-sibling::*/@name = @name)]">
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<!-- Process an element, and look for it's definition in one of the potential included files
if found, then copy that element definition in, an look for the the ComplexType defined by
the type attribute of the element
-->
<xsl:template match="xsd:element">
<xsl:variable name="elementName" select="@ref | @name"/>
<xsl:variable name="copyFieldMeta" select="document($metaLocation)/xsd:schema/xsd:element[@name = $elementName]"/>
<xsl:variable name="copyFieldField" select="document($fieldsLocation)/xsd:schema/xsd:element[@name = $elementName]"/>
<xsl:variable name="copyFieldComponent" select="document($componentsLocation)/xsd:schema/xsd:element[@name = $elementName]"/>
<xsl:variable name="copyFieldNoun" select="document($nounLocation)/xsd:schema/xsd:element[@name = $elementName]"/>
<xsl:choose>
<xsl:when test="$copyFieldField">
<xsl:copy-of select="$copyFieldField" copy-namespaces="no"/>
<xsl:apply-templates select="document($componentsLocation)/xsd:schema/xsd:complexType[@name = $copyFieldField/@type] | document($metaLocation)/xsd:schema/xsd:complexType[@name = $copyFieldField/@type]"/>
</xsl:when>
<xsl:when test="$copyFieldComponent">
<xsl:copy-of select="$copyFieldComponent" copy-namespaces="no"/>
<xsl:apply-templates select="document($componentsLocation)/xsd:schema/xsd:complexType[@name = $copyFieldComponent/@type] | document($metaLocation)/xsd:schema/xsd:complexType[@name = $copyFieldComponent/@type]"/>
</xsl:when>
<xsl:when test="$copyFieldMeta">
<xsl:copy-of select="$copyFieldMeta" copy-namespaces="no"/>
<xsl:apply-templates select="document($componentsLocation)/xsd:schema/xsd:complexType[@name = $copyFieldMeta/@type] | document($metaLocation)/xsd:schema/xsd:complexType[@name = $copyFieldMeta/@type]"/>
</xsl:when>
<xsl:when test="$copyFieldNoun">
<xsl:copy-of select="$copyFieldNoun" copy-namespaces="no"/>
<xsl:apply-templates select="document($nounLocation)/xsd:schema/xsd:complexType[@name = $copyFieldNoun/@type] | document($metaLocation)/xsd:schema/xsd:complexType[@name = $copyFieldNoun/@type] | document($componentsLocation)/xsd:schema/xsd:complexType[@name = $copyFieldNoun/@type]"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- Copy a found ComplexType into the result tree -->
<!-- Note this is where the infinite loop can occur -->
<!-- the code that checks for the name not equal to ref name works unless
the ref's name is not the same -->
<xsl:template match="xsd:complexType">
<xsl:variable name="complexName" select="substring-before(@name, 'Type')"/>
<xsl:copy-of select="." copy-namespaces="no"/>
<xsl:if test="descendant::xsd:extension">
<xsl:variable name="baseName" select="descendant::xsd:extension/@base"/>
<xsl:apply-templates select="document($componentsLocation)/xsd:schema/xsd:complexType[@name = $baseName] | document($metaLocation)/xsd:schema/xsd:complexType[@name = $baseName] | document($nounLocation)/xsd:schema/xsd:complexType[@name = $baseName]"/>
</xsl:if>
<xsl:apply-templates select="descendant::xsd:element[not(@ref = $complexName)]"/>
</xsl:template>
</xsl:stylesheet>



Any suggestions or ideas on how to avoid the recursion would be appreciated.


Current Thread