# RELAX NG schema for CellML 1.0
# Author: Jonathan Cooper
# Initial version created using Trang, and then modified
# for correctness and legibility.

# TODO:
#  - Tidy metadata checking

# Core CellML namespaces
namespace cellml = "http://www.cellml.org/cellml/1.0#"
namespace mathml = "http://www.w3.org/1998/Math/MathML"

# Metadata-related namespaces
namespace cmeta = "http://www.cellml.org/metadata/1.0#"
namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
namespace dc = "http://purl.org/dc/elements/1.1/"
namespace dcterms = "http://purl.org/dc/terms/"
namespace bqs = "http://www.cellml.org/bqs/1.0#"
namespace vCard = "http://www.w3.org/2001/vcard-rdf/3.0#"

# Our extension namespaces (URIs will change)
namespace pe = "https://chaste.ediamond.ox.ac.uk/cellml/ns/partial-evaluation"
namespace lut = "https://chaste.ediamond.ox.ac.uk/cellml/ns/lookup-tables"

# The 'no namespace' namespace, which is what will get used for
# most CellML attributes. Note the spec is broken in rule 2.5.2.
namespace local = ""

# Some general notes:
#  - rule 2.5.2 conflicts with the standard interpretation of XML namespaces.  Oops
#  - No CellML elements contain text content

# Valid CellML identifiers (rule 2.4.1)
# This is quite complex to get right.  The natural choice might
# be xsd:ID, but the lexical space isn't that of NCName: identifiers
# may start with a digit (yuck) and may not contain '.'.  Also
# identifiers are not always unique across a document (use the
# cmeta:id attribute for unique IDs).
ident = xsd:token { pattern = "_*[0-9a-zA-Z][_0-9a-zA-Z]*" }

oxygen-fix = [] empty # Work around oXygen's parser getting confused by the regexp above

# Extension namespaces are allowed (rule 2.4.3)
# Foreign elements should not contain CellML elements or attributes. We disallow this entirely,
# for technical reasons (id attribute conflicts).
foreign-nodes = ( foreign-attributes-non-local | foreign-elements)*
foreign-elements =
  element * - (cellml:* | rdf:* | mathml:*) {
    (foreign-elements* | foreign-attributes* | text) *
  }*
foreign-attributes-non-local =
  attribute * - (cellml:* | local:* | cmeta:id) { text }*
foreign-attributes =
  attribute * - (cellml:* | cmeta:id) { text }*

# All CellML elements may take this attribute, so that they can be
# referred to by metadata.
cmeta.id = attribute cmeta:id { xsd:ID }?

# Nodes that are common to every CellML element.
# Wrap these in parens so they're easier to add in.
common-nodes =
  (
    foreign-nodes
  & cmeta.id
  & rdf.RDF*
  )



start = cellml.model

cellml.model =
  element cellml:model {
    # Every model needs a name
    attribute name { ident }
    
    # Elements may appear in any order, although that given here
    # is recommended for CellML elements (rule 3.4.1).
  & cellml.units*
  & cellml.component*
  & cellml.group*
  & cellml.connection*
  & common-nodes
  }

# CellML components (rule 3.4.2)
cellml.component =
  element cellml:component {
    # Every component needs a (unique) name
    attribute name { ident }

    # Again, order is not required, but is recommended.
  & cellml.units*
  & cellml.variable*
    # The use of multiple reaction elements is allowed but
    # discoraged (7.4.1.1).
  & cellml.reaction*
  & mathml.math*
  & common-nodes
  }

# CellML variables (rule 3.4.3)
cellml.variable =
  element cellml:variable {
    # Each variable needs a name, which is unique within
    # the owning component (but not necessarily across a
    # whole model). 3.4.3.2
    attribute name { ident },
    # Every variable must declare its units.
    # This must be an identifier (5.4.2) corresponding to one
    # of the standard units or a user-defined unit within the
    # current model or component (3.4.3.3).
    attribute units { ident },
    # Interface values are optional, and can be one of
    # "in", "out" or "none" (default) (3.4.3.4 & 3.4.3.5).
    # They cannot both be "in" (4.3.4.6).
    (( 
      attribute private_interface { "in" },
      attribute public_interface { "out" | "none" }?
    ) | (
      attribute public_interface { "in" },
      attribute private_interface { "out" | "none" }?
    ) | (
      attribute private_interface { "out" | "none" }?,
      attribute public_interface { "out" | "none" }?,
      # An initial value must be a real number (3.4.3.7) and
      # may not be defined on a variable which has either
      # interface set to "in" (3.4.3.8)
      attribute initial_value { xsd:double }?
    )),

    common-nodes
  }

# The CellML connection element specifies maps between variables
# in different components.
# See rules 3.4.4, 3.4.5
cellml.connection =
  element cellml:connection {
    # Again order of elements is non-essential but recommended (3.4.4.1)

    # There must be exactly one map_components element (3.4.4.1)
    element cellml:map_components {
      # Components to map between must be specified (3.4.5.1).
      # They must refer to distinct (3.4.5.4) components within
      # the current model (3.4.5.2 & 3.4.5.3).
      # There must also be at most one connection between any 2 given
      # components.
      attribute component_1 { ident },
      attribute component_2 { ident },
      common-nodes
    } &
    # There must be at least one map_variables element (3.4.4.1)
    element cellml:map_variables {
      # Variables to map must be specified (3.4.6.1).
      # The variable_1 attribute must equal the name of a variable
      # defined within component_1 (3.4.6.2); similarly for
      # variable_2 (3.4.6.3).
      # Allowed mappings are determined by the encapsulation
      # hierarchy (3.4.6.4), which is beyond the scope of RELAX NG.
      attribute variable_1 { ident },
      attribute variable_2 { ident },
      common-nodes
    }+ &

    common-nodes
  }


# User-defined units (see section 5 of the specification).
cellml.units =
  element cellml:units {
    # All units must be named (5.4.1.1).
    # The name must not be equal one of the names defined in the
    # standard dictionary of units.  It must also be unique across
    # all units within its parent element. (5.4.1.2)
    attribute name { ident }
  & common-nodes

  & (
      (
        # If the base_units attribute has the value "yes" then only
        # foreign or metadata content is allowed (5.4.1.1).
        attribute base_units { "yes" }
      ) | (
        # If base_units is defined as "no" or is unspecified (5.4.1.3)
        # then the unit element must contain at least one units element
        # (5.4.1.1).
        attribute base_units { "no" }?
      & 
        ( # Complex units definition, or simple units without offset
          element cellml:unit {
            # All unit elements must define a units attribute (5.4.2.1),
            # which must reference a valid units name (5.4.2.2).
            # Circular definitions are not allowed.
            attribute units { ident },
            # The prefix, if present, must be an integer or a standard
            # prefix name (e.g. centi, which is equivalent to -2).  The
            # default is 0. (5.4.2.3)
            attribute prefix { xsd:integer | prefix-name }?,
            # The exponent, if present, must be a real number, and
	    # defaults to 1.0. (5.4.2.4)
            attribute exponent { xsd:double }?,
            # The multiplier, if present, must be a real number, and
            # defaults to 1.0. (5.4.2.5)
            attribute multiplier { xsd:double }?,
            # The offset, if present, must be a real number, and
            # defaults to 0.0. (5.4.2.6)
            # It must be omitted or zero except in a simple units
            # definition (5.4.2.7).
            attribute offset { xsd:decimal "0.0" }?,
            common-nodes
          }+
        | 
          # Simple units definition with offset (5.4.2.7)
          # These have a non-zero value for the offset attribute, and
          # only a single unit element.
          element cellml:unit {
            # All unit elements must define a units attribute (5.4.2.1),
            # which must reference a valid units name (5.4.2.2).
            # Circular definitions are not allowed.
            attribute units { ident },
            # The prefix, if present, must be an integer or a standard
            # prefix name (e.g. centi, which is equivalent to -2).  The
            # default is 0. (5.4.2.3)
            attribute prefix { xsd:integer | prefix-name }?,
            # The exponent, if present, must be a real number, and
            # defaults to 1.0. (5.4.2.4)
            # It must be 1.0 if offset is non-zero (5.4.2.7).
            attribute exponent { xsd:double "1.0" }?,
            # The multiplier, if present, must be a real number, and
            # defaults to 1.0. (5.4.2.5)
            attribute multiplier { xsd:double }?,
            # The offset, if present, must be a real number, and
            # defaults to 0.0. (5.4.2.6)
            # It must be omitted or zero except in a simple units
            # definition (5.4.2.7).
            attribute offset { xsd:decimal - xsd:decimal "0.0" }?,
            common-nodes
          }
        )
      )
    )
  }

# The set of names that may be used in the prefix attribute on a
# unit element.
prefix-name = 
  "yotta" | "zetta" | "exa" | "peta" | "tera" |
  "giga" | "mega" | "kilo" | "hecto" | "deka" |

  "deci" | "centi" | "milli" | "micro" | "nano" |
  "pico" | "femto" | "atto" | "zepto" | "yocto"


# Groups (see section 6 of the specification)
cellml.group =
  element cellml:group {
    # See 6.4.1.1
    cellml.relationship_ref+ &
    cellml.component_ref+ &
    common-nodes
  }

# 6.4.2: the relationship_ref element
cellml.relationship_ref =
  element cellml:relationship_ref {
    # There must be a relationship attribute, either in the 'CellML
    # namespace' (meaning no NS) or a user namespace (6.4.2.1)
    # I haven't figured out how to describe user-defined relationships
    # yet.  The best way may be to make the relationship attr optional,
    # but this would then allow invalid models.  TODO
    # For the CellML namespace case, the value must be "containment" or
    # "encapsulation" (6.4.2.2)
    (
      attribute relationship {  "encapsulation" }
    | 
      ( # For a non-encapsulation relationship, a name attribute is also
        # permitted (6.4.2.3) to name a hierarchy.
        attribute relationship {  "containment" },
        attribute name { ident }?
      )
    ) &
    common-nodes
  }

# 6.4.3: the component_ref element
cellml.component_ref =
  element cellml:component_ref {
    # The component attribute is mandatory; child elements are optional
    # (6.4.3.1).
    # The value of the component attribute should be the name of a
    # component defined in the current model (6.4.3.3)
    # The restrictions of 6.4.3.2 cannot be adequately expressed in
    # RELAX NG.
    attribute component { ident } &
    cellml.component_ref* &
    common-nodes
  }


# Reactions (see section 7 of the specification)
cellml.reaction =
  element cellml:reaction {
    # There may be a reversible attribute, which can take the values
    # "yes" or "no", and defaults to "yes" (7.4.1.2).  It is recommended
    # to always specify a value.
    attribute reversible { "yes" | "no" }? &
    # The recommended best practice is to define one variable_ref
    # element for each variable representing a chemical species that
    # participates in the reaction, and one variable_ref element for
    # the variable representing the rate of the reaction (7.4.1.1)
    cellml.variable_ref+ &
    common-nodes
  }

# 7.4.2: the variable_ref element
cellml.variable_ref =
  element cellml:variable_ref {
    # The variable attribute must have a value equal to the name of
    # a variable defined in the current component (7.4.2.2).  A
    # variable must only be referenced once in a given reaction.
    attribute variable { ident } &
    # There must be at least one role element (7.4.2.1).  The
    # recommended best practice is to define one role element for
    # each role assumed by the chemical species represented by the
    # referenced variable.
    (
      element cellml:role {
        (
          # The role attribute must take one of 7 values (7.4.3.2)
          attribute role {
            "catalyst" | "activator" | "inhibitor" |
            "modifier"  # Some unspecified effect
          },
          # The direction attribute defaults to "forward" (7.4.3.4),
          # indicating the role of this variable when the reaction
          # is running in the favoured direction.
          attribute direction { "forward" | "reverse" | "both" }?,
          # Usually this will be an integer, but fractional values
          # are allowed (7.4.3.6).
          attribute stoichiometry { xsd:decimal }?,
          (
            mathml.math*
          & common-nodes
          )
        ) | (
          # The role attribute must take one of 7 values (7.4.3.2)
          attribute role { "reactant" | "product" } &
          # Reactants and products must be described in the forward
          # direction (7.4.3.5).
          attribute direction { "forward" }? &
          ((
            # This must refer to the name of a variable in the current
            # component (7.4.3.7).  It must be unique across all role
            # elements within the current component.
            # It can only occur if the role is "reactant" or "product"
            # (7.4.3.8).
            attribute delta_variable { ident } &
            # When a delta_variable attribute is present, there must
            # be either a stoichiometry attribute or at least one math
            # element (7.4.3.8).
            (
              # Usually this will be an integer, but fractional values
              # are allowed (7.4.3.6).
              attribute stoichiometry { xsd:decimal }
            | mathml.math+
            )
          ) | (
            attribute stoichiometry { xsd:decimal }?,
            mathml.math*
          )) &
          common-nodes
        )
      }+
    |
      element cellml:role {
        # The role attribute must take one of 7 values (7.4.3.2).
        # If the value is "rate" then the reference variable
        # represents the reaction rate, and no other roles can be
        # assigned to it.  Further, the delta_variable and
        # stoichiometry attributes are disallowed (7.4.3.3).
        attribute role { "rate" },
        # Rates must be specified in the forward direction (7.4.3.5)
        attribute direction { "forward" }?,
        (
          mathml.math*
        & common-nodes
        )
      }
    ) &
    common-nodes
  }





######################################################################
#            Mathematics (section 4 of the specification)            #
######################################################################

# Almost any of MathML 2.0 is permitted.
# There is however a CellML subset,
# which is all that is required of conformant processing software.

# This part uses a schema generated from the MathML 2.0 DTD to
# define the contents of math elements.


include "mathml2.rnc" {
  # Restrict the attribute list for math elements, since in a CellML
  # document they're not top-level.
  attlist-mathml.math = MATHML.Common.attrib

  # Presentation markup is NOT allowed except within annotation-xml.
  # However, that element may contain any XML, so we can just define
  # the presentation elements to not exist :)
  PresInCont = empty
    
  # We don't want to allow empty expressions
  ContentExpression = Content+

  # Restrict what is allowable "any XML" so ID attrs don't conflict
  any =
    (element mathml:* {
       attribute mathml:* { text }*,
       any
     }
     | text)*

  # We could restrict the content of ci to be a CellML identifier:
  # mathml.ci = element mathml:ci {
  #   attlist-mathml.ci, ident
  # }
  # However, since later it will be checked that each ci element contains
  # the name of an existing variable, there isn't much point.

  # Check numbers of operands using co-occurrence constraints
  mathml.apply =
    element mathml:apply { attlist-mathml.apply, ApplyContentExpression }

  # I think some operators are wrongly classified in the MathML schema
  # Lets be more specific about the 'misc' operators
  copmisc = empty
}
  # Extra unary operators that were misc.
  cop1ary |= mathml.root
    | mathml.log
    | mathml.minus
    | cseqop
    | cstatopmoment
    | clogicopquant
  # Be more specific for calculus operators
  ccalcop1ary |= mathml.int
    | mathml.diff
    | mathml.partialdiff
    | mathml.divergence
    | mathml.grad
    | mathml.curl
    | mathml.laplacian
  ccalcop2ary = mathml.partialdiff
  
  # Extra binary operators that were misc.
  cop2ary |= ccalcop2ary | mathml.minus

  # Restrict number of operands depending on the operator
  c_operand = ctoken | c0ary | cconstructor | cspecial | cother
  c_not_oper = cquantifier | csemantics

  ApplyContentExpression = (
    (cop1ary, c_operand) & c_not_oper*
  ) | (
    ((crel2ary | cop2ary), c_operand, c_operand) & c_not_oper*
  ) | (
    ((crelnary | copnary), c_operand+) & c_not_oper*
  )


# All cn elements must contain a cellml:units attribute
attlist-mathml.cn &= attribute cellml:units { ident }

# Allow binding time & lookup table annotations
MATHML.Common.attrib &=
   attribute (pe:* | lut:*) {text}*





######################################################################
#                              Metadata                              #
######################################################################

# This Schema doesn't say anything about allowable metadata.

rdf-any = (
  element * - (mathml:* | cellml:*) {
    attribute * {text}*,
    rdf-any*
  } | text )

rdf.RDF =
  element rdf:RDF {
    attribute * { text }*,
    rdf-any*
  }
