[xsl] XSLT Recursive grouping - can't determine next group

Subject: [xsl] XSLT Recursive grouping - can't determine next group
From: "Kyle Partridge" <kpartridge@xxxxxxxxxxxx>
Date: Tue, 6 Apr 2004 18:19:06 -0400
Hi everybody,

I'm trying to write a set of templates to do recursive grouping.

The xml looks like this:
   <regions>
	<region region-id="7" left="180" top="8.25" width="85.5"
height="228">
		Region 3
     	</region>	
	<region region-id="4" left="48" top="38.25" width="84.75"
height="36">
		Region 1
	</region>
	<region region-id="6" left="30" top="104.25" width="84"
height="48">
		Region 2
	</region>
	<region region-id="8" left="24" top="272.25" width="38.25"
height="48">
		Region 4
	</region>
	<region region-id="9" left="108" top="278.25" width="38.25"
height="60">
		Region 5
	</region>
   </regions>

The xml I want is this:
  <regions>
    <region-set>
      <region-data-summary region-id="7" left="180" top="8.25"
right="265.5" bottom="236.25"/>
      <region region-id="7" left="180" top="8.25" width="85.5"
height="228">
		Region 3
      </region>
      <region-data-summary region-id="4" left="48" top="38.25"
right="132.75" bottom="74.25"/>
      <region region-id="4" left="48" top="38.25" width="84.75"
height="36">
		Region 1
      </region>
      <region-data-summary region-id="6" left="30" top="104.25"
right="114" bottom="152.25"/>
      <region region-id="6" left="30" top="104.25" width="84"
height="48">
		Region 2
      </region>
    </region-set>
    <region-set>
	<region-data-summary region-id="8" left="24" top="272.25"
right="62.25" bottom="320.25"/>
      <region region-id="8" left="24" top="272.25" width="38.25"
height="48">
		Region 4
      </region>
      <region-data-summary region-id="9" left="108" top="278.25"
right="146.25" bottom="338.25"/>
      <region region-id="9" left="108" top="278.25" width="38.25"
height="60">
		Region 5
      </region>
    </region-set>
  </regions>

In my XSLT (below), I'm trying to use Jeni Tennison's recursive grouping
method (XSLT and XPath on the Edge, pp. 204, 205) - but something's not
working because I can't seem to figure out how to determine which region
to process next, after I've finished creating my region-set...

This code will create the first region-set shown in the desired XML
above, no problem.  But then I can't figure out how to apply-templates
in order to start the node-set gathering process over on region 4
(region-id=8).  A region-set ends when the region being tested does not
sit "beside" (see xslt template of same name for definition, below)
either of the two regions that preceded it.  This is triggered by
region4 (region-id=8), but after the "item" mode template returns, there
is no way for me to know that.

I hope this is making some kind of logical sense?  Can someone please
help shed some light on what I'm doing wrong here?

Many thanks,
KP


<xsl:template match="regions">
	<regions>
		<xsl:apply-templates select="region[1]" mode="group"/>
	</regions>
</xsl:template>


<xsl:template match="region" mode="group">
	<xsl:variable name="next-region"
select="following-sibling::region[1]"/>
	<xsl:if test="$next-region!=''">
		<xsl:variable name="this-beside-next">
			<xsl:call-template name="beside">
				<xsl:with-param name="region1"
select="."/>
				<xsl:with-param name="region2"
select="$next-region"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:choose>
			<xsl:when test="$this-beside-next=1">
				<region-set>
					<region-data-summary
region-id="{@region-id}" left="{@left}" top="{@top}"
right="{@left+@width}" bottom="{@top+@height}"/>
					<xsl:copy-of select="."/>
					<region-data-summary
region-id="{$next-region/@region-id}" left="{$next-region/@left}"
top="{$next-region/@top}"
right="{$next-region/@left+$next-region/@width}"
bottom="{$next-region/@top+$next-region/@height}"/>
					<xsl:copy-of
select="$next-region"/>
					<!-- skip straight to testing
the region after next-region -->
					<xsl:apply-templates
select="following-sibling::region[2]" mode="item"/>
				</region-set>
			</xsl:when>
			<xsl:otherwise>
				<!-- HOW DO I KNOW WHICH REGION TO TEST
NEXT?? -->
			</xsl:otherwise>
		</xsl:choose>
	</xsl:if>
</xsl:template>

<xsl:template match="region" mode="item">
	<xsl:variable name="this-in-group">
		<xsl:variable name="this-beside-comp1">
			<xsl:call-template name="beside">
				<xsl:with-param name="region1"
select="."/>
				<xsl:with-param name="region2"
select="preceding-sibling::region[1]"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:variable name="this-beside-comp2">
			<xsl:call-template name="beside">
				<xsl:with-param name="region1"
select="."/>
				<xsl:with-param name="region2"
select="preceding-sibling::region[2]"/>
			</xsl:call-template>
		</xsl:variable>
		<xsl:value-of
select="number($this-beside-comp1)+number($this-beside-comp2)"/>
	</xsl:variable>
	
	<xsl:choose>
		<xsl:when test="$this-in-group&gt;0">
			<region-data-summary region-id="{@region-id}"
left="{@left}" top="{@top}" right="{@left+@width}"
bottom="{@top+@height}"/>
			<xsl:copy-of select="."/>
			<xsl:apply-templates
select="following-sibling::region[1]" mode="item"/>
		</xsl:when>
		<xsl:otherwise/>
	</xsl:choose>
</xsl:template>

<xsl:template name="in-shadow-of">
	<!-- Determine whether or not region1 begins or ends (or is
contained) within the area of region2 -->
	<xsl:param name="region1"/>
	<xsl:param name="region2"/>
	<xsl:choose>
		<!-- EITHER top of region2 >= top of region1 AND the top
of region2 <= bottom of region1 -->
		<xsl:when test="$region2/@top &gt;= $region1/@top and
$region2/@top &lt;= ($region1/@top + $region1/@height)">
			<xsl:value-of select="number('1')"/>
		</xsl:when>
		<!-- OR bottom of region2 >= top of region1 and bottom
of region2 <= bottom of region1 -->
		<xsl:when test="($region2/@top + $region2/@height) &gt;=
$region1/@top and ($region2/@top + $region2/@height) &lt;=
($region1/@top + $region1/@height)">
			<xsl:value-of select="number('1')"/>
		</xsl:when>
		<!-- OR region2 is not within the shadow of region1 -->
		<xsl:otherwise><xsl:value-of
select="number('0')"/></xsl:otherwise>
	</xsl:choose>
</xsl:template>
	
<xsl:template name="beside">
	<!-- Determine whether or not region1 and region2 are next to
each other -->
	<xsl:param name="region1"/>
	<xsl:param name="region2"/>
	<xsl:variable name="one-covers-two">
		<xsl:call-template name="in-shadow-of">
			<xsl:with-param name="region1"
select="$region1"/>
			<xsl:with-param name="region2"
select="$region2"/>
		</xsl:call-template>
	</xsl:variable>
		
	<xsl:choose>
		<xsl:when test="$one-covers-two=1">1</xsl:when>
		<xsl:otherwise>
			<xsl:variable name="two-covers-one">
				<xsl:call-template name="in-shadow-of">
					<xsl:with-param name="region1"
select="$region2"/>
					<xsl:with-param name="region2"
select="$region1"/>
				</xsl:call-template>
			</xsl:variable>
			<xsl:choose>
				<xsl:when
test="$two-covers-one=1">1</xsl:when>
				<xsl:otherwise>0</xsl:otherwise>
			</xsl:choose>
		</xsl:otherwise>
	</xsl:choose>
</xsl:template>

Current Thread