<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (c) 2010 DeltaXML Ltd.  All rights reserved. -->
<!-- This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License version 3 only,
    as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License version 3 for more details
    (a copy is included in the LICENSE-LGPL.txt file that accompanied this code).

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/> -->

<!-- $Id: delta-constraints.sch 6804 2010-07-23 15:41:58Z tristanm $ -->
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <ns prefix="delta" uri="http://www.deltaxml.com/ns/track-changes/delta-namespace"/>
  <ns prefix="split" uri="http://www.deltaxml.com/ns/track-changes/split-namespace"/>
  <ns prefix="ac" uri="http://www.deltaxml.com/ns/track-changes/attribute-change-namespace"/>
  <!-- separate patterns are needed here, if a single referencing pattern is used then if an element has more than one of these attrs
      then only one rule is triggered as any give node in the input tree only matches a single rule within a pattern -->
  <pattern id="referencing1">
    <rule context="*[@delta:insertion-change-idref]" id="insertion-ct-refs">
      <assert test="count(//delta:change-transaction[@delta:change-id=current()/@delta:insertion-change-idref]) eq 1">
        The change-idref must reference exactly one change-transaction
      </assert>
    </rule>
    <rule context="delta:change-transaction" id="ct-usage">
      <let name="insertion-cts" value="//@delta:insertion-change-idref"/>
      <let name="removal-cts" value="//@delta:removal-change-idref"/>
      <let name="att-change-cts" value="for $att in //@ac:* return substring-before($att, ',')"/>
      <assert test="not(empty(($insertion-cts[. eq current()/@delta:change-id], 
                               $removal-cts[. eq current()/@delta:change-id],
                               $att-change-cts[. eq current()/@delta:change-id])))">
        Every change transaction must be referenced at least once </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing2">
    <rule context="*[@delta:removal-change-idref]" id="removal-ct-refs">
      <assert test="count(//delta:change-transaction[@delta:change-id=current()/@delta:removal-change-idref]) eq 1"> 
        The change-idref must reference exactly one change-transaction 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing3">
    <rule context="*[@delta:end-element-idref]" id="end-element-refs-forwards">
      <assert test="count(//delta:remove-leaving-content-end[@delta:end-element-id=current()/@delta:end-element-idref]) eq 1">
        The end-element-idref must reference exactly one end-element
      </assert>
    </rule>
    <rule context="*[@delta:end-element-id]" id="end-element-refs-backwards">
      <assert test="count(//delta:remove-leaving-content-start[@delta:end-element-idref=current()/@delta:end-element-id]) eq 1">
        The end-element-id must be referenced by exactly one start-element 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing4">
    <rule context="*[@ac:*]" id="attribute-ct-refs">
      <assert test="every $attr in current()/@ac:* satisfies 
                      count(//delta:change-transaction[@delta:change-id=substring-before($attr, ',')]) eq 1">
        Every ac:* attribute must start with a reference to a change transaction </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing5">
    <rule context="delta:inserted-text-start" id="inserted-text-ref1">
      <assert test="count(//delta:inserted-text-end[@delta:inserted-text-end-id eq current()/@delta:inserted-text-end-idref]) eq 1">
        Every delta:inserted-text-start must reference exactly one delta:inserted-text-end 
      </assert>
    </rule>
    <rule context="delta:inserted-text-end" id="inserted-text-ref2">
      <assert test="count(//delta:inserted-text-start[@delta:inserted-text-end-idref eq current()/@delta:inserted-text-end-id]) eq 1">
        Every delta:inserted-text-end must be referenced by exactly one delta:inserted-text-start </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing6">
    <rule context="*[@delta:move-id]" id="move-id-ref1">
      <assert test="count(//@delta:move-idref[. eq current()/@delta:move-id]) gt 0"> 
        Every @delta:move-id must be referenced at least once 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="referencing7">
    <rule context="*[@delta:move-idref]" id="move-id-ref2">
      <assert test="count(//@delta:move-id[. eq current()/@delta:move-idref]) eq 1"> 
        Every @delta:move-idref must reference exactly one @delta:move-id 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="splits">
    <rule context="*[@delta:split-id]" id="split-id">
      <assert test="count(//*[@split:*[. eq current()/@delta:split-id]]) eq 1"> 
        A split-id must be referenced by exactly one split attribute 
      </assert>
    </rule>
    <rule context="*[@split:*]" id="split-refs">
      <!-- The test: multiple-changes/paragraph-multiple-split is an example of an element with multple split:* attributes -->
      <assert test="every $attr in current()/@split:* satisfies 
                      count(//*[@delta:insertion-type='split'][@delta:split-id=$attr]) eq 1">
        An attribute in the split namespace must reference one split 
      </assert>
    </rule>
  </pattern>
  
  <!-- TODO:  Disabled while discussing nature of this constraint 
    
  <pattern id="move-ids">
    <rule context="delta:merge">
      <assert test="every $c in (delta:partial-content,delta:intermediate-content//*,delta:trailing-partial-content) satisfies
                      exists($c/@delta:move-id)"></assert>
    </rule>
  </pattern>
  -->
  <pattern id="uniqueness1">
    <rule context="delta:tracked-changes" id="tracked-change-id">
      <assert test="count(delta:change-transaction) eq count(distinct-values(delta:change-transaction/@delta:change-id))">
        change transaction change-ids must be unique 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="uniqueness2">
    <rule context="/*" id="split-id-uniqueness">
      <assert test="count(//@delta:split-id) eq count(distinct-values(//@delta:split-id))"> 
        delta:split-id attributes must be unique 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="uniqueness3">
    <rule context="/*" id="end-element-id-uniqueness">
      <assert test="count(//@delta:end-element-id) eq count(distinct-values(//@delta:end-element-id))">
        delta:end-element-id attributes must be unique
      </assert>
    </rule>
  </pattern>
  
  <pattern id="uniqueness4">
    <rule context="/*" id="insert-text-end-id-uniqueness">
      <assert test="count(//@delta:inserted-text-end-id) eq count(distinct-values(//@delta:inserted-text-end-id))"> 
        delta:inserted-text-end-id attributes must be unique 
      </assert>
    </rule>
  </pattern>
  
  <pattern id="ordering1">
    <!-- check that cts within an insert-with-content occurred after the insertion -->
    <rule context="*[@delta:insertion-type='insert-with-content'][*//@delta:insertion-change-idref or *//@delta:removal-change-idref]" id="inserted-then-changed">
      <!-- the change-transaction of the current matched element -->
      <let name="correspondingct" value="//delta:change-transaction[@delta:change-id eq current()/@delta:insertion-change-idref]"/>
      <!-- all change-idref attributes on descendants of the current matched element -->
      <let name="enclosed-ctrefs" value="current()/*//@delta:insertion-change-idref, current()/*//@delta:removal-change-idref"/>
      <!-- all change-transactions whose id matches one of the attributes in $enclosed-ctrefs -->
      <let name="enclosed-cts" value="//delta:change-transaction[@delta:change-id = $enclosed-ctrefs]"/>
      <assert test="not(empty($enclosed-ctrefs)) and 
                    not(empty($enclosed-cts)) and 
                    (every $ct in $enclosed-cts satisfies $ct &gt;&gt; $correspondingct)">
        An added item may contain changes within it, but the changes must all be after it was added 
      </assert>
    </rule>
    <rule context="delta:change-transaction-set | delta:change-transaction-stack" id="ct-group-definition">
      <assert test="every $ctr in delta:change-transaction-references 
                      satisfies //delta:change-transaction[@delta:change-id eq $ctr/@idref] &lt;&lt; current()">
        All the members of a  set/stack must be previously defined CT or CT groups</assert>
    </rule>
  </pattern>
  
  <pattern id="ordering2">
    <!-- check that cts within a remove-with-content occurred before the removal -->
    <rule context="delta:removed-content[*//@delta:insertion-change-idref or *//@delta:removal-change-idref]" id="removed-after-changes">
      <!-- the change-transaction of the current matched element (the removal) -->
      <let name="removal-ct" value="//delta:change-transaction[@delta:change-id eq current()/@delta:removal-change-idref]"/>
      <!-- all change-idref attributes on descendants of the current matched element -->
      <let name="enclosed-ctrefs" value="current()/*//@delta:insertion-change-idref, current()/*//@delta:removal-change-idref"/>
      <!-- all change-transactions whose id matches one of the attributes in $enclosed-ctrefs -->
      <let name="enclosed-cts" value="//delta:change-transaction[@delta:change-id = $enclosed-ctrefs]"/>
      <assert test="not(empty($enclosed-ctrefs)) and
                    not(empty($enclosed-cts)) and
                    (every $ct in $enclosed-cts satisfies $ct &lt;&lt; $removal-ct)"> 
        A deleted item may contain changes within it, but the changes must all be before it was deleted </assert>
    </rule>
  </pattern>
  
  <pattern id="ordering3">
    <!-- check that an element that is added and deleted was added BEFORE being deleted -->
    <rule context="delta:remove-leaving-content-start[*[@delta:insertion-change-idref]]" id="insert-then-rlc-order">
      <!-- the change-transaction of the current matched element (the removal) -->
      <let name="removal-ct" value="//delta:change-transaction[@delta:change-id eq current()/@delta:removal-change-idref]"/>
      <!-- the change-transaction of the child element (the inserion) -->
      <let name="insertion-ct" value="//delta:change-transaction[@delta:change-id eq current()/*/@delta:insertion-change-idref]"/>
      
      <assert test="$insertion-ct &lt;&lt; $removal-ct">
        An element insertion must occur before its deletion (remove-leaving-content)
      </assert>
    </rule>
    <rule context="delta:removed-content[*[@delta:insertion-change-idref]]" id="insert-then-remove-order">
      <!-- the change-transaction of the current matched element (the removal) -->
      <let name="removal-ct" value="//delta:change-transaction[@delta:change-id eq current()/@delta:removal-change-idref]"/>
      <!-- the change-transaction of the child element (the inserion) -->
      <let name="insertion-ct" value="//delta:change-transaction[@delta:change-id eq current()/*/@delta:insertion-change-idref]"/>
      
      <assert test="$insertion-ct &lt;&lt; $removal-ct">
        An element insertion must occur before its deletion (remove-with-content)
      </assert>
    </rule>
  </pattern>
  
  <pattern id="attribute-structure">
    <rule id="attribute-syntax" context="*[@ac:*]">
      <assert test="every $attr in @ac:* satisfies (matches($attr, '^\c+,insert,\c+$') or 
                                                    matches($attr, '^\c+,modify,\c+,.*$') or 
                                                    matches($attr, '^\c+,remove,\c+,.*$'))">
        There are syntactic rules for the comma separated attribute change info
      </assert>
    </rule>
  </pattern>
  
  <!-- NOTE the following test is only guaranteed to be true if there is no breaks in the tracking of changes to a document.
       As such, it has been removed from the constraints -->
  <!--<pattern id="attribute-removal">
    <rule context="*[@ac:*[matches(., '^\c+,remove,\c+,.*$')]]" id="check-removed-att">
      <!-\- all removed attributes -\->
      <let name="removed-atts" value="current()/@ac:*[matches(., '^\c+,remove,\c+,.*$')]"/>
      <!-\- all removed att names -\->
      <let name="removed-att-names" value="for $att in $removed-atts return substring-before(substring-after($att, ',remove,'), ',')"/>
      <!-\- all change transactions of removed atts -\->
      <let name="removed-att-cts" value="for $att in $removed-atts return //delta:change-transaction[@delta:change-id eq substring-before($att, ',')]"/>
      
      <assert test="for $i in 1 to count($removed-atts) return 
                      not(exists(current()/@*[name(.) eq $removed-att-names[$i]]))
                      or
                      (some $att in current()/@ac:*[matches(., '^\c+,insert,\c+$')]
                                                   [substring-after(., ',insert,') eq $removed-att-names[$i]]
                            satisfies //delta:change-transaction[@delta:change-id eq substring-before($att, ',')] &gt;&gt; $removed-att-cts[$i])">
        Removed attributes should no longer exist on the element they were removed from (unless they have been reinserted in a later ct)
      </assert>
    </rule>
  </pattern>-->
  
  <!-- NOTE: The following needs to be implemented in a similar way to the test above.
       i.e. every inserted attribute should exist on the elemnt unless it has since been removed 
       Again, this is only guaranteed to be true if there are no breaks in the tracking of changes -->
  <!--<pattern id="attribute-insertion">
    <rule context="*[@ac:*[matches(., '^\c+,insert,\c+$')]]" id="check-inserted-att">
      <assert test="true()"/>
    </rule>
  </pattern>-->
</schema>
