<?xml version="1.0" encoding="utf-8"?>
<iso:schema 
  xmlns="http://purl.oclc.org/dsdl/schematron"
  xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
  xmlns:dp="http://www.pnml.org/version-2009/grammar/pnml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  queryBinding='xslt2'
  schemaVersion='ISO19757-3'
  see='http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html'
  defaultPhase='#ALL'>
 <iso:title>A Schematron mini-schema for PNML constraints specification</iso:title>
  <iso:ns prefix='dp' uri='http://www.pnml.org/version-2009/grammar/pnml'/>
  <p> 
    This schema is the first of a set or Schematron schemas, which provide sets of 
    Schematron rules you may use to further check the validity of a PNML Document, 
    in addition to the original PNML Schema in RELAX NG. So you may use these rules
    complementary to validating a PNML Document against its grammar.
    
    This document provides constraints on the Core Model and PT nets. First, some basic
    checks on some important attributes are performed. Then, some OCL constraints
    stated in the PNML standard that are not captured in PNML Schema are translated
    into Schematron rules.
    
    Schematron is the third part of the ten-part International Standard 
    ISO/IEC 19757, related to XML technologies for data model specification and
    validation.
    
    File name                      : CMconstraints.sch
    Version                        : 2010
    Copyright notice               : Copyright 2010 Lom Hillah (AFNOR)
    License                        : GNU GPL v3 (See http://www.gnu.org/licenses/)
    Revisions  					           -
    (Number/Date/Author/[Comment]) : 1/August 13, 2009/L.H./Inception
                                   : 2/March 2, 2010/L.H + C.D./Optimization with HashMap of Nodes:Ids
  </p>
  
  <iso:phase 
    id="CoreModel"
    see="http://www.pnml.org/version-2009/grammar/pnmlcoremodel.rng">
    <p>
      Phases are used for progressive validation. Each phase we provide is 
      basically about a Petri net type. This schema provides a phase for Core Models. 
      If you extend this schema with another phase, you may set the phase you just
      want for validation: set the value of the @defaultPhase attribute
      of the 'iso:schema' element to the id of the desired phase. 
      For example a typical extension may involve your tool specific elements.
      
      Each phase may activate several rule patterns. It's up to you if you want
      all patterns be activated, or just the ones you are interested in.
      In that case, use XML classic comment tag to deactivate those patterns
      you don't want to use.
    </p>
    
    <iso:active pattern="CheckIds"/>
    <iso:active pattern="CheckIdUniquess"/>
    <iso:active pattern="CheckArcSource"/>
    <iso:active pattern="CheckArcTarget"/>
    <iso:active pattern="CheckArcSourceRef"/>
    <iso:active pattern="CheckArcTargetRef"/>
    <iso:active pattern="CheckArcRefsOnSamePage"/>
    <iso:active pattern="CheckRefNoderef"/> 
    <iso:active pattern="CheckRefPlaceRef"/>
    <iso:active pattern="CheckRefPlaceRefType"/>
    <iso:active pattern="CheckRefTransitionRef"/>
    <iso:active pattern="CheckRefTransitionRefType"/>
    <!-- Experimental-->
    <iso:active pattern="CheckRefNodeCycle"/> 
    <!-- Reports number of objects in the net(s) -->
    <iso:active pattern="ReportNumNetsAndNodes"/>  
  </iso:phase>
  
  <xsl:key name="nodeId" match="dp:place|dp:transition|dp:referencePlace|dp:referenceTransition" use="@id"/>
 
  <!-- Abstract patterns -->
  <iso:pattern abstract="true" id="isAttr">
    <title>Checks that a certain required attribute is present</title>
    <iso:rule context="$elem"> 
      <iso:assert test="$att"> 
        A node of type '<name/>' must have a $att attribute.
      </iso:assert> 
    </iso:rule> 
  </iso:pattern>
  
  <iso:pattern abstract="true" id="isRef">
    <title>Checks that a certain attribute is referencing an actual node in the document</title>
    <iso:rule context="$cont"> 
      <iso:assert test="$attref" diagnostics="refNodes"> 
        The $att attribute of node <value-of select="@id"/> of type '<name/>' must refer to an existing node.
      </iso:assert> 
    </iso:rule> 
  </iso:pattern>
  
  <!-- Patterns for @id attributes -->
  <iso:pattern is-a="isAttr" id="CheckIds">
    <title>Checks id attribute existence</title>
    <iso:param name="elem" value="dp:net|dp:page|dp:place|dp:transition|
      dp:arc|dp:referencePlace|dp:referenceTransition"/> 
    <iso:param name="att"  value="@id"/> 
  </iso:pattern>
  
 <iso:pattern id="CheckIdUniquess">
   <title>Checks id attribute uniqueness</title>
   <iso:rule context="*[@id]">
     <iso:assert test="count(//@id[. = current()/@id]) = 1">
       The @id attribute value <value-of select="current()/@id"/> of the node of 
       type '<name/>' is not unique.
     </iso:assert>
   </iso:rule>
 </iso:pattern>
  
  <!-- Patterns for arcs -->
  <iso:pattern is-a="isAttr" id="CheckArcSource">
    <title>Checks arcs's source attribute existence</title>
    <iso:param name="elem" value="dp:arc"/> 
    <iso:param name="att"  value="@source"/> 
  </iso:pattern>
  
  <iso:pattern is-a="isAttr" id="CheckArcTarget">
    <title>Checks arcs's target attribute existence</title>
    <iso:param name="elem" value="dp:arc"/> 
    <iso:param name="att"  value="@target"/> 
  </iso:pattern>
  
  <iso:pattern is-a="isRef" id="CheckArcSourceRef">
    <title>Checks arcs's source attribute references an existing node</title>
    <iso:param name="cont" value="dp:arc[@source]"/> 
    <iso:param name="attref"  value="key('nodeId', ./@source)"/> 
    <iso:param name="att" value="@source"/> 
  </iso:pattern>
  
  <iso:pattern is-a="isRef" id="CheckArcTargetRef">
    <title>Checks arcs's target attribute references an existing node</title>
    <iso:param name="cont" value="dp:arc[@target]"/> 
    <iso:param name="attref"  value="key('nodeId', current()/@target)"/> 
    <iso:param name="att" value="@target"/> 
  </iso:pattern>
  
  <iso:pattern id="CheckArcRefsOnSamePage">
    <title>Checks if the source and target referenced nodes are 
      on the same page as this arc
    </title>
    <iso:rule context="dp:arc[@source and @target]">
      <iso:assert test="key('nodeId', current()/@target)/parent::dp:page/@id = parent::dp:page/@id">
        The 'target' node of arc <value-of select="current()/@id"/> node must be on the same page as this arc.
      </iso:assert>
      <iso:assert test="key('nodeId', current()/@source)/parent::dp:page/@id = parent::dp:page/@id">
        The 'source' node of arc <value-of select="current()/@id"/> must be on the same page as this arc.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <!-- Patterns for Reference nodes -->
  <iso:pattern is-a="isAttr" id="CheckRefNoderef">
    <title>Checks reference nodes' ref attribute existence</title>
    <iso:param name="elem" value="dp:referencePlace|dp:referenceTransition"/> 
    <iso:param name="att"  value="@ref"/> 
  </iso:pattern>
  
  <iso:pattern is-a="isRef" id="CheckRefPlaceRef">
    <title>Checks reference places' ref attribute references an exiting node</title>
    <iso:param name="cont" value="dp:referencePlace[@ref]"/>
    <iso:param name="attref"  value="key('nodeId', current()/@ref)"/> 
    <iso:param name="att"  value="@ref"/> 
  </iso:pattern>
  
  <iso:pattern id="CheckRefPlaceRefType">
    <title>Checks reference places' ref attribute references a place or another reference place</title>
    <iso:rule context="dp:referencePlace[@ref]">
      <iso:assert test="key('nodeId', current()/@ref)/(self::dp:place|self::dp:referencePlace)[@id = current()/@ref]">
        The referenced node from reference place <value-of select="@id"/> must be a place or a referencePlace.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <iso:pattern is-a="isRef" id="CheckRefTransitionRef">
    <title>Checks reference transitions' ref attribute references an existing node</title>
    <iso:param name="cont" value="dp:referenceTransition[@ref]"/>
    <iso:param name="attref"  value="key('nodeId', current()/@ref)"/> 
    <iso:param name="att"  value="@ref"/> 
  </iso:pattern>
  
  <iso:pattern id="CheckRefTransitionRefType">
    <title>Checks reference transitions' ref attribute references a transition or
      another reference transition
    </title>
    <iso:rule context="dp:referenceTransition[@ref]">
      <iso:assert test="key('nodeId', current()/@ref)/(self::dp:transition|self::dp:referenceTransition)[@id = current()/@ref]">
        The referenced node from reference transition <value-of select="@id"/> must be a 
        transition or a referenceTransition.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <!-- This pattern is Experimental. You may deactivate it.-->
  <iso:pattern id="CheckRefNodeCycle">
    <title>No cyclic reference among self reference nodes (Just first level of cyclic reference)</title>
    <iso:rule context="dp:referencePlace|dp:referenceTransition">
      <let name="referencedNodes" value="(key('nodeId', current()/@ref))
          [@id = current()/@ref]"/>
      <iso:assert test="count($referencedNodes[@id = current()/@id]) = 0">
        The reference node <value-of select="@id"/> of type '<name/>' is referencing itself.
      </iso:assert>
    </iso:rule>
  </iso:pattern>
  
  <!-- There is no need to design rule patterns for graphics, because their relationship
  with their parents or ancestor nodes are structurally enforced by PNML Schema.
  -->
  
  <!-- Some reports -->
  <iso:pattern id="ReportNumNetsAndNodes">
     <iso:rule context="/">
       <let name="nets" value="//dp:net"/>
       <let name="places" value="//dp:place"/>
       <let name="transitions" value="//dp:transition"/>
       <let name="arcs" value="//dp:arc"/>
       <let name="referencePlaces" value="//dp:referencePlace"/>
       <let name="referenceTransitions" value="//dp:referenceTransition"/>
       <iso:report test="count($nets) > 0">
         This document contains <value-of select="count($nets)"/> net(s).
       </iso:report>
       <iso:report test="count($places) > 0">
         This document contains <value-of select="count($places)"/> place(s).
       </iso:report>
       <iso:report test="count($transitions) > 0">
         This document contains <value-of select="count($transitions)"/> transition(s).
       </iso:report>
       <iso:report test="count($arcs) > 0">
         This document contains <value-of select="count($arcs)"/> arc(s).
       </iso:report>
       <iso:report test="count($referencePlaces) > 0">
         This document contains <value-of select="count($referencePlaces)"/> reference place(s).
       </iso:report>
       <iso:report test="count($referenceTransitions) > 0">
       This document contains <value-of select="count($referenceTransitions)"/> reference transition(s).
       </iso:report>
     </iso:rule>
  </iso:pattern>
  
  <!-- Diagnotics-->
  <iso:diagnostics> 
    <iso:diagnostic id="refNodes"> 
      In the case of a referencePlace, it must refer to a place or another referencePlace.
      In the case of a referenceTransition, it must refer to a transition or another
      referenceTransition. Cyclic references are forbidden.
    </iso:diagnostic> 
  </iso:diagnostics> 
 
</iso:schema>

