<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: deltaV2.sch 4865 2009-02-26 12:00:14Z chrisc $ -->
<!-- Copyright (c) 2008 DeltaXML Ltd.  All rights reserved. -->
<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <ns prefix="deltaxml" uri="http://www.deltaxml.com/ns/well-formed-delta-v1"/>
  
  <pattern id="deltaCheck">
    <rule context="/*">
      <assert test="@deltaxml:version='2.0'">Delta version must be 2.0</assert>
      <assert test="@deltaxml:content-type=('full-context', 'changes-only', 'merge-concurrent')">There must be a deltaxml:content-type attribute with one of the values 'full-context', 'changes-only' or 'merge-concurrent'</assert>
    </rule>
    <rule context="*[@deltaxml:deltaV2]">
      <report test="@deltaxml:deltaV2=''">Delta values may not be empty</report>
      <report test="matches(@deltaxml:deltaV2, '[^ABC!=]')">Delta value contains invalid characters</report>
    </rule>
  </pattern>

  <pattern id="globalRules">
    
    <rule context="*[ancestor::*[not(self::deltaxml:*)][@deltaxml:deltaV2][1][not(contains(@deltaxml:deltaV2,'!='))]]
      [not(self::deltaxml:*)]
      [not(parent::deltaxml:attributes)]">
      <assert test="not(@deltaxml:deltaV2)">An element that is a descendant of a non-modified element cannot have a delta attribute</assert>
    </rule> 
    
    <rule context="*[contains(@deltaxml:deltaV2,'!=')]/*">
      <assert test="@deltaxml:deltaV2">All child elements of an element with @deltaxml:deltaV2 containing '!=' must have a delta attribute</assert>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2]">
      <report test="ancestor::*[not(@deltaxml:deltaV2)]">If an element contains a delta attribute all the elements up to the root of the tree should also have delta attributes</report>
    </rule>
    
    <rule context="*[contains(@deltaxml:deltaV2,'A')]">
      <report test="ancestor::*[not(contains(@deltaxml:deltaV2, 'A'))]">If an element contains A in its delta, all ancestors up to the root of the tree should also contain A</report>
    </rule>
    <rule context="*[contains(@deltaxml:deltaV2,'B')]">
      <report test="ancestor::*[not(contains(@deltaxml:deltaV2, 'B'))]">If an element contains B in its delta, all ancestors up to the root of the tree should also contain B</report>
    </rule>
    <rule context="*[contains(@deltaxml:deltaV2,'C')]">
      <report test="ancestor::*[not(contains(@deltaxml:deltaV2, 'C'))]">If an element contains C in its delta, all ancestors up to the root of the tree should also contain C</report>
    </rule>
  </pattern>  

  <pattern id="elementRules">
    <!-- are these rules applicable to all elements in a delta including deltaxml:textGroup, deltaxml:attributes
      (and their children) or just the actual real elements -->
    <rule context="*[@deltaxml:deltaV2='A!=B']">
      <assert test="child::*[@deltaxml:deltaV2=('A', 'B', 'A!=B')]">An element with delta A!=B requires at least one child with delta A, B or A!=B</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('A', 'B', 'A!=B', 'A=B'))]">Permitted child attributes of A!=B must be one of A, B, A!=B or A=B</report>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='A!=C']">
      <assert test="child::*[@deltaxml:deltaV2=('A', 'C', 'A!=C')]">An element with delta A!=C requires at least one child with delta A, C or A!=C</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('A', 'C', 'A!=C', 'A=C'))]">Permitted child attributes of A!=C must be one of A, C, A!=C or A=C</report>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='B!=C']">
      <assert test="child::*[@deltaxml:deltaV2=('B', 'C', 'B!=C')]">An element with delta B!=C requires at least one child with delta B, C or B!=C</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('B', 'C', 'B!=C', 'B=C'))]">Permitted child attributes of B!=C must be one of B, C, B!=C or B=C</report>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='A!=B!=C']">
      <!-- it may be possible to simplify this test further... -->
      <assert test="child::*[@deltaxml:deltaV2='A'] and child::*[@deltaxml:deltaV2='B'] or
                    child::*[@deltaxml:deltaV2='A'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='B'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='A=B'] and child::*[@deltaxml:deltaV2='A=C'] or
                    child::*[@deltaxml:deltaV2='A=B'] and child::*[@deltaxml:deltaV2='B=C'] or
                    child::*[@deltaxml:deltaV2='A=C'] and child::*[@deltaxml:deltaV2='B=C'] or
                    child::*[@deltaxml:deltaV2='A=B'] and child::*[@deltaxml:deltaV2='A'] or
                    child::*[@deltaxml:deltaV2='A=B'] and child::*[@deltaxml:deltaV2='B'] or
                    child::*[@deltaxml:deltaV2='A=C'] and child::*[@deltaxml:deltaV2='A'] or
                    child::*[@deltaxml:deltaV2='A=C'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='B=C'] and child::*[@deltaxml:deltaV2='B'] or
                    child::*[@deltaxml:deltaV2='B=C'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='A!=B'] or
                    child::*[@deltaxml:deltaV2='A!=C'] or
                    child::*[@deltaxml:deltaV2='B!=C'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='A'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='B'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='A=C'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='B=C'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='A!=B=C'] or
                    child::*[@deltaxml:deltaV2='A=B!=C'] and child::*[@deltaxml:deltaV2='A=C!=B'] or
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='B'] or
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='A=B'] or
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='A=C'] or
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='A=B!=C'] or 
                    child::*[@deltaxml:deltaV2='A!=B=C'] and child::*[@deltaxml:deltaV2='A=C!=B'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='A'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='C'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='A=B'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='B=C'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='A=B!=C'] or
                    child::*[@deltaxml:deltaV2='A=C!=B'] and child::*[@deltaxml:deltaV2='A!=B=C'] or
                    child::*[@deltaxml:deltaV2='A!=B!=C']">An element with delta A!=B!=C must have children with at least one of the delta value combinations specified in the test calculation</assert>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='A=B!=C']">
      <assert test="child::*[@deltaxml:deltaV2=('C', 'A=B', 'A=B!=C')]">An element with delta A=B!=C requires at least one child with delta C, A=B or A=B!=C</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('C', 'A=B', 'A=B!=C', 'A=B=C'))]">Permitted child attributes of A=B!=C are: C, A=B, A=B!=C or A=B=C</report>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='A!=B=C']">
      <assert test="child::*[@deltaxml:deltaV2=('A', 'B=C', 'A!=B=C')]">An element with delta A!=B=C requires at least one child with delta A, B=C or A!=B=C</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('A', 'B=C', 'A!=B=C', 'A=B=C'))]">Permitted child attributes of A!=B=C are: A, B=C, A!=B=C or A=B=C</report>
    </rule>
    
    <rule context="*[@deltaxml:deltaV2='A=C!=B']">
      <assert test="child::*[@deltaxml:deltaV2=('B', 'A=C', 'A=C!=B')]">An element with delta A=C!=B requires at least one child with delta B, A=C or A=C!=B</assert>
      <report test="child::*[not(@deltaxml:deltaV2=('B', 'A=C', 'A=C!=B', 'A=B=C'))]">Permitted child attributes of A=C!=B are: B, A=C, A=C!=B or A=B=C</report>
    </rule>
  </pattern>
  
  <pattern id="attributeRules">
    
    <rule context="*[parent::deltaxml:attributes]">
      <assert test="deltaxml:attributeValue">All elements representing attributes (i.e. child of deltaxml:attributes) must contain at least one deltaxml:attributeValue child</assert>
      <report test="*[not(self::deltaxml:attributeValue)]">For an element representing an attribute (i.e. child of deltaxml:attributes), the only child elements allowed are deltaxml:attributeValue elements</report>
      <assert test="namespace-uri() ne ''">All elements representing attributes  (i.e. child of deltaxml:attributes) must be associated with a namespace</assert>
      <report test="count(tokenize(ancestor::*[2]/@deltaxml:deltaV2, '=|!=')) eq count(tokenize(current()/@deltaxml:deltaV2, '=|!='))
                                   and count(deltaxml:attributeValue) eq 1">A attribute should not be represented using deltaxml:attributes if it does not indicate change relative to its parent element</report>
      <report test="contains(@deltaxml:deltaV2, 'A') and count(deltaxml:attributeValue[contains(@deltaxml:deltaV2, 'A')]) ne 1">An element representing an attribute, whose deltaV2 contains A, must have only one deltaxml:attributeValue child whose deltaV2 contains A</report>
      <report test="contains(@deltaxml:deltaV2, 'B') and count(deltaxml:attributeValue[contains(@deltaxml:deltaV2, 'B')]) ne 1">An element representing an attribute, whose deltaV2 contains B, must have only one deltaxml:attributeValue child whose deltaV2 contains B</report>
      <report test="contains(@deltaxml:deltaV2, 'C') and count(deltaxml:attributeValue[contains(@deltaxml:deltaV2, 'C')]) ne 1">An element representing an attribute, whose deltaV2 contains C, must have only one deltaxml:attributeValue child whose deltaV2 contains C</report>
    </rule>
   
    <rule context="deltaxml:attributeValue">
      <assert test="ancestor::*[2][self::deltaxml:attributes]">A deltaxml:attributeValue element must be the grandchild of a deltaxml:attributes element</assert>
      <assert test="count(node())=count(text())">A deltaxml:attributeValue element must contain only text node(s)</assert>
      <assert test="count(text())&lt;=1">A deltaxml:attributeValue element must contain either 0 or 1 text node</assert>
      <assert test="not(contains(@deltaxml:deltaV2,'!='))">A deltaxml:attributeValue element must have a deltaV2 value that expresses equality</assert>
      
      <report test="preceding-sibling::*[@deltaxml:deltaV2=current()/@deltaxml:deltaV2] or 
                    following-sibling::*[@deltaxml:deltaV2=current()/@deltaxml:deltaV2]">A deltaxml:attributeValue element must not have a sibling with the same delta value</report>
    </rule>
    <rule context="deltaxml:attributes">
      <report test="preceding-sibling::*">A deltaxml:attributes element must be the first child of its parent element </report>
    </rule>
  </pattern>
  
  <pattern id="textRules"> 
    <rule context="deltaxml:textGroup">
      <report test="child::*[not(self::deltaxml:text)]">textGroup cannot contain anything other than deltaxml:text elements</report>
      <report test="following-sibling::*[1][self::deltaxml:textGroup[@deltaxml:deltaV2=current()/@deltaxml:deltaV2]]">Consecutive textGroups should not have the same delta value</report>
      <assert test="deltaxml:text">A deltaxml:textGroup must contain at least one deltaxml:text child</assert>
      
      <report test="count(tokenize(parent::*/@deltaxml:deltaV2, '=|!=')) eq count(tokenize(current()/@deltaxml:deltaV2, '=|!='))
        and count(deltaxml:attributeValue) eq 1">A textGroup should not be used if it does not indicate change relative to its parent element</report>
      <report test="contains(@deltaxml:deltaV2, 'A') and count(deltaxml:text[contains(@deltaxml:deltaV2, 'A')]) ne 1">A textGroup, whose deltaV2 contains A, must have only one text child whose deltaV2 contains A</report>
      <report test="contains(@deltaxml:deltaV2, 'B') and count(deltaxml:text[contains(@deltaxml:deltaV2, 'B')]) ne 1">A textGroup, whose deltaV2 contains B, must have only one text child whose deltaV2 contains B</report>
      <report test="contains(@deltaxml:deltaV2, 'C') and count(deltaxml:text[contains(@deltaxml:deltaV2, 'C')]) ne 1">A textGroup, whose deltaV2 contains C, must have only one text child whose deltaV2 contains C</report>
    </rule>

    <rule context="deltaxml:text">  
      <assert test="count(node()) = count(text())">A deltaxml:text element can only contain text nodes</assert>
      <assert test="count(text())&lt;=1">A deltaxml:text element must contain either 0 or 1 text node</assert>
      <report test="contains(@deltaxml:deltaV2, '!=')">A deltaxml:text element must have a deltaV2 value that expresses equality</report>
      <assert test="parent::deltaxml:textGroup">A deltaxml:text element must be a child of deltaxml:textGroup</assert>
    </rule>
  </pattern>
</schema>
