Subject: RELAX NG Schema for draft...-01 Atom

From: Norman Walsh <[email-masked]>
Date: Tue, 20 Jul 2004 09:52:51 -0400

Dave and I are still working, but here's a snapshot that I believe is
correct. (It passes my smoke test, anyway.)

# -*- Relax NG -*-

namespace local = ""
namespace atom = "http://purl.org/atom/ns#draft-ietf-atompub-format-01"
namespace s = "http://www.ascc.net/xml/schematron"

start = atomFeed | atomEntry

# Attribute definitions

atomCommonAttributes =
   attribute xml:base { atomUri }?,
   attribute xml:lang { atomLanguageTag }?

atomVersionAttribute = attribute version { text }

# Common Atom Constructs

atomContentConstruct =
   atomCommonAttributes,
   attribute type { atomRegisteredMediaType }?,
   attribute mode { "xml" | "escaped" | "base64" }?,
   (text|anyElement)*

atomPersonConstruct =
   atomCommonAttributes,
   (element atom:name { text }
    & element atom:uri { atomUri }?
    & element atom:email { atomEmailAddress }?)

atomDateConstruct =
   atomCommonAttributes,
   xsd:dateTime

atomLinkConstruct =
   atomCommonAttributes,
   attribute rel {
      "alternate"
    | "start"
    | "next"
    | "prev"
    | "service.edit"
    | "service.post"
    | "service.feed" },
   attribute type { atomMediaType },
   attribute href { atomUri },
   attribute hreflang { atomLanguageTag }?,
   attribute title { text }?,
   empty

# atom:feed
# TODO: Test for multiple atom:link/@rel='alternate' with the same @type
# The following tests are simple to do, but my validator is giving me trouble.
# TODO: Debug and add them back
#       Test for at least one atom:link/@rel='alternate'
#       Test for atom:author or all atom:entry have atom:author

atomFeed =
   [
      s:rule [
         context = "/atom:feed"
         s:assert [
            test = "atom:head/atom:link[@rel='alternate']"
            "An atom:feed must have at least one link element with a rel attribute of 'alternate'."
         ]
      ]
      s:rule [
         context = "/atom:feed"
         s:assert [
            test = "atom:head/atom:author or not(atom:entry[count(atom:author) = 0])"
            "An atom:feed must have an atom:author unless all of its atom:entry children have an atom:author."
         ]
      ]
   ]
   element atom:feed {
      atomCommonAttributes,
      atomVersionAttribute,
      atomHead,
      (atomEntry|anyForeignElement)*
   }

atomHead = element atom:head {
   atomCommonAttributes,
   (atomTitle
    & atomLink+
    & atomAuthor?
    & atomContributor*
    & atomTagline?
    & atomId?
    & atomGenerator?
    & atomCopyright?
    & atomInfo?
    & atomModified)
}

# atom:title

atomTitle = element atom:title { atomContentConstruct }

# atom:link

atomLink = element atom:link { atomLinkConstruct }

# atom:author

atomAuthor = element atom:author { atomPersonConstruct }

# atom:contributor

atomContributor = element atom:contributor { atomPersonConstruct }

# atom:tagline

atomTagline = element atom:tagline { atomContentConstruct }

# atom:id

atomId = element atom:id { atomUri }

# atom:generator

atomGenerator = element atom:generator {
   atomCommonAttributes,
   atomVersionAttribute?,
   attribute uri { atomUri }?,
   text
}

# atom:copyright

atomCopyright = element atom:copyright { atomContentConstruct }

# atom:info

atomInfo = element atom:info { atomContentConstruct }

# atom:modified
# TODO: Test for a timezone that SHOULD be UTC

atomModified = element atom:modified { atomDateConstruct }

# atom:entry
# TODO: Test for multiple atom:link @rel='alternate' with the same @type

atomEntry =
   [
      s:rule [
         context = "/atom:entry"
         s:assert [
            test = "@version"
            "The version attribute is required on standalone atom:entry elements."
         ]
      ]
      s:rule [
         context = "atom:entry"
         s:assert [
            test = "atom:link[@rel='alternate']"
            "An atom:entry must have at least one link element with a rel attribute of 'alternate'."
         ]
      ]
      s:rule [
         context = "atom:entry"
         s:assert [
            test = "atom:author or ../atom:author"
            "An atom:entry must have an atom:author if the parent atom:feed does not."
         ]
      ]
   ]
   element atom:entry {
      atomCommonAttributes,
      atomVersionAttribute?,
      (atomTitle
       & atomLink+
       & atomAuthor?
       & atomContributor*
       & atomId?
       & atomModified
       & atomIssued?
       & atomCreated?
       & atomSummary?
       & atomContent?
       & atomCopyright?
       & atomOrigin?
       & anyForeignElement*)
   }

# atom:issued

atomIssued = element atom:issued { atomDateConstruct }

# atom:created
# TODO: Test for a timezone that SHOULD be UTC

atomCreated = element atom:created { atomDateConstruct }

# atom:summary

atomSummary = element atom:summary { atomContentConstruct }

# atom:content

atomContent =
   [
      s:rule [
         context = "atom:content"
         s:assert [
            test = "@type != 'multipart/alternative' or not(@mode)"
            "Multipart/alternative content cannot have a mode attribute."
         ]
      ]
      s:rule [
         context = "atom:content"
         s:assert [
            test = "@type = 'multipart/alternative' and *[@type = 'multipart/alternative']"
            "Multipart/alternative content cannot have multipart/alternative children."
         ]
      ]
      s:rule [
         context = "atom:content"
         s:assert [
            test = "@type != 'multipart/alternative' or child::*"
            "Multipart/alternative content must have children content elements."
         ]
      ]
   ]
   element atom:content { atomContentConstruct }

# atom:origin

atomOrigin = element atom:origin { atomUri }

# Low-level simple types

# TODO: can anything more specific be said about these types?

atomMediaType = text
atomRegisteredMediaType = text
atomLanguageTag = text
atomUri = text
atomEmailAddress = text

# Extensibility

anyForeignElement =
   element * - (atom:* | local:*)
   {
      (attribute * { text }
       | text
       | anyForeignElement)*
   }

anyForeignAttribute =
   attribute * - (atom:* | local:* | xml:*) { text }

anyElement =
   element * - local:*
   {
      (attribute * { text }
       | text
       | anyElement)*
   }

# EOF

                                        Be seeing you,
                                          norm

-- 
Norman Walsh <[email-masked]> | 'All is vanity,' saith the Preacher.
http://nwalsh.com/            | But if all were only vanity, who would
                              | mind? Alas, it is too often worse than
                              | vanity: agony, darkness, death
                              | also.--Thomas Hardy