Check Overlapping Time Periods with Schematron

This should cover W3C XML Schema, Relax NG and DTD related problems.
Sepp C
Posts: 3
Joined: Tue Mar 06, 2018 12:02 pm

Check Overlapping Time Periods with Schematron

Post by Sepp C » Tue Mar 13, 2018 5:32 pm

Hi,

I have a very similar problem to topic15574.html as I need to check for overlapping time periods with Schematron.

I have following additional constraints:
  • - Time Periods are grouped by an ID
  • - The overlap check only applies on a per-group basis. This means that between unrelated groups overlaps are allowed
  • - The End element is optional. Because of this I cannot use the solution from the other thread. Oxygen runs in a Stack-Overflow Exception because of the recursive call.
  • - When the end element is missing, it must be treated as '9999-12-31'
My schema looks like this, this would be a valid input file:

Code: Select all


<periods>
<period>
<id>1</id>
<start>2017-01-01</start>
</period>
<period>
<id>1</id>
<start>2000-01-01</start>
<end>2010-12-31</end>
</period>
<period>
<id>2</id>
<start>2000-01-01</start>
<end>2017-12-31</end>
</period>
</periods>
Thanks for any help

tavy
Posts: 179
Joined: Thu Jul 01, 2004 12:29 pm

Re: Check Overlapping Time Periods with Schematron

Post by tavy » Wed Mar 14, 2018 3:39 pm

Hello,

To avoid the Stack-Overflow Exception you need to collect the start and end date from the 'start' and 'end' element instead of the attributes. Something like in the example below.
For the other changes that you want to make in the function, unfortunately I cannot help you. I suggest you to write on the XSL-List, or on Stackoverflow.

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">

<sch:ns prefix="fct" uri="localFunctions"/>

<sch:pattern>
<sch:rule context="periods">
<sch:let name="overlapping" value="fct:checkOverlaping((),period)"/>
<sch:assert test="$overlapping=''">
Overlapping period: <sch:value-of select="$overlapping"/></sch:assert>
</sch:rule>
</sch:pattern>

<xsl:function name="fct:checkOverlaping" >
<xsl:param name="currentEndDate" as="xs:date?"/>
<xsl:param name="periods" as="node()*"/>
<xsl:choose>
<xsl:when test="not(exists($currentEndDate))">
<xsl:variable name="orderedPeriods" as="node()*">
<xsl:for-each select="$periods">
<xsl:sort select="start"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="fct:checkOverlaping(xs:date($orderedPeriods[position()=1]/end),$orderedPeriods[position()>1])"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="
if (not(exists($periods))) then '' else
if ($currentEndDate > $periods[position()=1]/start)
then concat('End: ', $currentEndDate, ' Start: ', $periods[position()=1]/start) else
fct:checkOverlaping($periods[position()=1]/end,$periods[position()>1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</sch:schema>
Best Regards,
Octavian
Octavian Nadolu
<oXygen/> XML Editor
http://www.oxygenxml.com

Sepp C
Posts: 3
Joined: Tue Mar 06, 2018 12:02 pm

Re: Check Overlapping Time Periods with Schematron

Post by Sepp C » Wed Mar 14, 2018 4:42 pm

Hi Octavian

I had changed the attibutes @ to elements already (of course). Anyway, found a solution. Had to interject my wished maxvalue directly in the calling of the function, like that:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">

<sch:ns prefix="fct" uri="localFunctions"/>

<sch:pattern>
<sch:rule context="periods">
<sch:let name="overlapping" value="fct:checkOverlaping((),period)"/>
<sch:assert test="$overlapping=''">
Overlapping period: <sch:value-of select="$overlapping"/></sch:assert>
</sch:rule>
</sch:pattern>

<xsl:function name="fct:checkOverlaping" >
<xsl:param name="currentEndDate" as="xs:date?"/>
<xsl:param name="periods" as="node()*"/>
<xsl:choose>
<xsl:when test="not(exists($currentEndDate))">
<xsl:variable name="orderedPeriods" as="node()*">
<xsl:for-each select="$periods">
<xsl:sort select="@start"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
==> <xsl:value-of select="fct:checkOverlaping(if (exists($orderedPeriods[position() = 1]/validUntil)) then xs:date($orderedPeriods[position() = 1]/validUntil) else xs:date('9999-12-31'), $orderedPeriods[position() > 1])"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="
if (not(exists($periods))) then '' else
if ($currentEndDate > $periods[position()=1]/@start)
then concat('End: ', $currentEndDate, ' Start: ', $periods[position()=1]/@start) else
fct:checkOverlaping($periods[position()=1]/@end,$periods[position()>1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</sch:schema>
Thanks!

Post Reply