<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    queryBinding="xslt2">
    <ns prefix="deltaxml" uri="http://www.deltaxml.com/ns/well-formed-delta-v1"/>
    <ns prefix="saxon" uri="http://saxon.sf.net/"/>
    
    <include href="../../../projects/core/source/product/schematron/delta-common.sch#COMMON-root-rules"/>
    
    <xsl:include href="../../../projects/core/source/product/schematron/delta-2-functions.xsl"/>
    
    <xsl:variable name="delta-version-order" select="tokenize(/*/@deltaxml:version-order, ', ')" as="xs:string*" />
    
    <pattern id="simplified-merge-root-checks">
        <rule id="simplified-merge-content-type" context="/*">
            <include href="deltaV2-merge.sch#V2-merge-version-order"/>
            <assert id="V2-version-value"
                test="@deltaxml:version = 's1.0'">Delta version must be s1.0</assert>
            <assert id="content-type-value"
                test="@deltaxml:content-type = ('simplified-merge-concurrent')">There must be a deltaxml:content-type attribute with the value 'simplified-merge-concurrent'.</assert>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-versionSet">
        <rule id="eqaul-version-set" context="*[@deltaxml:versionSet]">
            <report id="empty-versionSet"
                test="@deltaxml:versionSet=''">versionSet values may not be empty</report>
            <report id="equal-versionSet"
                test="contains(@deltaxml:versionSet,'!=')">Only equal deltaxml:versionSet versions are allowed.</report>
            <assert id="versionSet-value-format"
                test="every $v in (deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) satisfies
                $v castable as xs:NMTOKEN">The following versionSet versions must be instances of NMTOKEN: <value-of 
                    select="deltaxml:report-string-join(
                    (for $v in (deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) return
                    if ($v castable as xs:NMTOKEN) then () else $v
                    ), 'and'
                    )"/>.</assert>
            <assert id="duplicate-version"
                test="count(distinct-values(tokenize(@deltaxml:versionSet, '='))) eq 
                count(tokenize(@deltaxml:versionSet, '='))">Delta versions must not be repeated within a versionSet attribute</assert>
            
            <assert test="every $v in (deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) satisfies $v = $delta-version-order"
                >Invalid version(s) detected for element at path <value-of select="saxon:path(.)"/>: <xsl:value-of 
                    select="deltaxml:report-string-join(
                    (for $v in (deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) return 
                    if ($v = $delta-version-order) then () else $v
                    ),
                    'and')" />.</assert>
            
            <assert test="count(deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) = count(distinct-values(deltaxml:getDeltaV2Versions(@deltaxml:versionSet)))"
                >VersionSet value contains duplicate versions for element at path <value-of select="saxon:path(.)"/>: <value-of 
                    select="deltaxml:report-string-join(
                    (for $v in (deltaxml:getDeltaV2Versions(@deltaxml:versionSet)) return 
                    if (count($delta-version-order[.=$v]) le 1) then () else $v
                    ),
                    'and')" /> .</assert>
            
            <assert test="every $ec in (deltaxml:getDeltaV2VersionEquivClasses(@deltaxml:versionSet)) satisfies
                deltaxml:areVersionsInOrder($ec, $delta-version-order)"
                >Invalid ordering of versionSet version(s) between equalities '=' detected for element at path <value-of select="saxon:path(.)"/>: equivalence class(es) <value-of 
                    select="deltaxml:report-string-join(
                    (for $ec in (deltaxml:getDeltaV2VersionEquivClasses(@deltaxml:versionSet)) return 
                    if (deltaxml:areVersionsInOrder($ec, $delta-version-order)) then () else $ec
                    ),
                    'and')" />' have invalid version orderings.</assert>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-version-group-checks">
        <rule context="deltaxml:versionContentGroup">
            <report id="two-mandatory-versionContent" 
                test="count(deltaxml:versionContent) lt 2">All elements representing version group (i.e. child of deltaxml:versionContentGroup) must contain at least
                two deltaxml:versionContent children.</report>
            <report id="version-group-child-test" 
                test="*[not(self::deltaxml:versionContent)]">A versionContentGroup cannot contain anything other than deltaxml:versionContent elements.</report>
            <assert id="version-group-versionSet-check"
                test="not(exists(@deltaxml:versionSet))">Version group should not have deltaxml:versionSet attribute.</assert>
            <assert id="version-group-child-versionSet-check"
                test="count(distinct-values(for $v in deltaxml:versionContent/@deltaxml:versionSet return tokenize($v,'='))) = count($delta-version-order)"
                >Version content group must have child elements specifying all the versions.</assert>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-version-attribute-group-checks">
        <rule context="deltaxml:versionAttributeGroup">
            <report id="two-mandatory-versionAttribute" 
                test="count(deltaxml:versionAttribute) lt 2">All elements representing version attribute group (i.e. child of deltaxml:versionAttributeGroup) 
                must contain at least two deltaxml:versionAttribute children.</report>
            <report id="version-attribute-group-child-test" 
                test="*[not(self::deltaxml:versionAttribute)]">A versionAttributeGroup cannot contain anything other than deltaxml:versionAttribute elements.</report>
            <report id="version-attribute-groups-position"
                test="preceding-sibling::*">A deltaxml:versionAttributeGroup elements must appear just after its parent element.</report>
            <assert id="duplicate-version-attribute-groups"
                test="empty(following-sibling::deltaxml:versionAttributeGroup)">Only one deltaxml:versionAttributeGroup is allowed in an element.</assert>
            <assert id="version-attribute-group-versionSet-check"
                test="not(exists(@deltaxml:versionSet))">Version attribute group should not have deltaxml:versionSet attribute.</assert>
            <assert id="version-attribute-group-child-versionSet-check"
                test="count(distinct-values(for $v in deltaxml:versionAttribute/@deltaxml:versionSet return tokenize($v,'='))) = count($delta-version-order)"
                >Version attribute group must have child elements specifying all the versions.</assert>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-version-content-checks">
        <rule context="deltaxml:versionContent">
            <assert id="version-content-versionSet-check"
                test="exists(@deltaxml:versionSet)">Every version content element should have a versionSet.</assert>
            <assert id="version-content-parent"
                test="parent::deltaxml:versionContentGroup">A deltaxml:versionContent element must be a child of deltaxml:versionContentGroup.</assert>
            <report id="version-content-in-group-single-entry-per-version"
                test="some $version in tokenize(@deltaxml:versionSet, '=') satisfies 
                preceding-sibling::*[tokenize(@deltaxml:versionSet, '=|!=') = $version] or
                following-sibling::*[tokenize(@deltaxml:versionSet, '=|!=') = $version]">
                Each versionSet version should occur exactly once in consecutive deltaxml:versionContent elements.</report>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-version-attribute-checks">
        <rule context="deltaxml:versionAttribute">
            <assert id="version-attribute-versionSet-check"
                test="exists(@deltaxml:versionSet)">Every version attribute element should have a versionSet.</assert>
            <assert id="version-attribute-empty-check"
                test="empty(node())">Every version attribute element should be empty.</assert>
            <report id="unchanged-attributes-check"
                test="count(tokenize(@deltaxml:versionSet, '=')) eq count($delta-version-order)">Unchanged attributes should
                be on the elements.</report>
            <assert id="version-Attribute-parent"
                test="parent::deltaxml:versionAttributeGroup">A deltaxml:versionAttribute element must be a child of deltaxml:versionAttributeGroup.</assert>
            <report id="version-attribute-in-group-single-entry-per-version"
                test="some $version in tokenize(@deltaxml:versionSet, '=') satisfies 
                preceding-sibling::*[tokenize(@deltaxml:versionSet, '=|!=') = $version] or
                following-sibling::*[tokenize(@deltaxml:versionSet, '=|!=') = $version]">
                Each versionSet version should occur exactly once in consecutive deltaxml:versionAttribute elements.</report>
        </rule>
    </pattern>
    
    <pattern id="simplified-merge-element-rules">
        <rule context="*[not(self::deltaxml:versionContent) and not(self::deltaxml:versionAttribute)]">
            <report id="element-versionSet-check"
                test="@deltaxml:versionSet">The element at path <value-of 
                    select="saxon:path(.)"/> having a versionSet value is neither deltaxml:versionContent nor deltaxml:versionAttribute.</report>
        </rule>
    </pattern>
    
</schema>