<?xml-stylesheet href="http://www.w3.org/StyleSheets/base.css" type="text/css"?>
<?xml-stylesheet href="http://www.w3.org/2002/02/style-xsl.css" type="text/css"?>
<xsl:stylesheet 
    xmlns:xsl  ="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:h    ="http://www.w3.org/1999/xhtml"
    xmlns:dc   ="http://purl.org/dc/elements/1.1/"
    xmlns:rdf  ="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:rdfs ="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:owl  ="http://www.w3.org/2002/07/owl#"
    xmlns:dt   ="http://www.w3.org/2001/XMLSchema#"
    xmlns:spec ="http://www.w3.org/2002/12/cal/icalSpec#"
    xmlns:cal  ="http://www.w3.org/2002/12/cal/icaltzd#"
    >

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Transforming the iCalendar spec to an RDF/OWL ontology</title>
    <link rel="stylesheet" href="http://www.w3.org/StyleSheets/base"/>
  </head>
  <body>
    <div class='head'><a href="/"><img src='/Icons/w3c_home' alt='W3C'/></a></div>
    <h1>Transforming the iCalendar spec to an RDF/OWL ontology</h1>

    <p>This transformation extracts an RDF/XML schema/ontology
    from an XHTML representation of RFC 2445, iCalendar.</p>

    <p>It's intended for use with <a
    href="http://www.w3.org/2004/01/rdxh/spec">GRDDL</a> (see also: <a
    href="http://www.w3.org/2003/11/rdf-in-xhtml-demo">demo</a>.)</p>

<p>@@TODO: check the output for OWL-DL-happiness, ala
<a href="http://dannyayers.com/archives/002257.html">Danny A 5Feb</a>.
capture cardinality constraints; write test cases that fail them;
get pellet and/or surnia to find the problem.</p>

    <address>
      <a href="http://www.w3.org/People/Connolly/">Dan Connolly</a> <br />
      <small>$Id: webize2445.xsl,v 1.23 2007/06/28 18:21:54 connolly Exp $</small>
    </address>
  </body>
</html>

<xsl:output method="xml" indent="yes"/>

<xsl:variable name="this"
	      select='"http://www.w3.org/2002/12/cal/icaltzd"' />

<xsl:template match="h:html/h:body">
  <rdf:RDF xml:base="{$this}">

    <owl:Thing rdf:about="{$this}">
      <dc:source>
        <owl:Thing rdf:about="http://www.ietf.org/rfc/rfc2445.txt"/>
      </dc:source>
      <owl:versionInfo>$Id: webize2445.xsl,v 1.23 2007/06/28 18:21:54 connolly Exp $</owl:versionInfo>
      <owl:versionInfo>subject to change with notice to www-rdf-calendar@w3.org</owl:versionInfo>
      <rdfs:seeAlso rdf:resource="http://www.w3.org/2002/12/cal/"/>
      <rdfs:seeAlso rdf:resource="http://lists.w3.org/Archives/Public/www-rdf-calendar/"/>
      <rdfs:seeAlso rdf:resource="http://esw.w3.org/topic/RdfCalendar"/>
    </owl:Thing>

    <owl:AnnotationProperty rdf:about="http://www.w3.org/2002/12/cal/icalSpec#valueType"/>
    <owl:AnnotationProperty rdf:about="http://www.w3.org/2002/12/cal/icalSpec#valueListType"/>
    <owl:ObjectProperty rdf:about="http://purl.org/dc/elements/1.1/source"/>

    <xsl:for-each select='h:div/h:dl[h:dd/@class="ComponentName"]'>
      <xsl:variable name='id' select='h:dt[@id]/@id'/>
      <xsl:variable name='name'
		    select='normalize-space(h:dd[@class="ComponentName"])'/>
      <xsl:variable name='label'>
	<xsl:choose>
	  <xsl:when test='$name = "VEVENT"'>
	    <xsl:text>Event</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "VTODO"'>
	    <xsl:text>To-do</xsl:text>
	  </xsl:when>
	</xsl:choose>
      </xsl:variable>

      <xsl:variable name='purpose'
		    select='normalize-space(h:dd[@class="Purpose"])'/>

      <!-- perhaps make icalSpec:Component a subclass of rdfs:Class? -->
      <!-- that would take us outside OWL DL... let's not do that yet -->
      <owl:Class rdf:ID='{$id}'>
	<xsl:if test="string-length($label) &gt; 0">
	  <rdfs:label><xsl:value-of select='$label'/></rdfs:label>
	</xsl:if>
	<rdfs:comment><xsl:value-of select='$purpose'/></rdfs:comment>

	<!-- find properties that can be specified on this component -->
	<xsl:for-each select='//h:div/h:dl[
		      .//h:a[@rel="applies-to" and @href=concat("#", $id)]
		      ]'>
	  <xsl:variable name='propid' select='h:dt[@id]/@id'/>
	  <rdfs:subClassOf>
	    <owl:Restriction>
	      <owl:onProperty rdf:resource='{concat("#", $propid)}'/>
	      <owl:minCardinality
	       rdf:datatype='http://www.w3.org/2001/XMLSchema#integer'
	       >0</owl:minCardinality>
	    </owl:Restriction>
	  </rdfs:subClassOf>

	  <!-- exdate, recurrence-id apply everywhere that rrule does -->
	  <xsl:if test='$propid = "rrule"'>
	    <rdfs:subClassOf>
	      <owl:Restriction>
		<owl:onProperty rdf:resource='{concat("#", "exdate")}'/>
		<owl:minCardinality
		 rdf:datatype='http://www.w3.org/2001/XMLSchema#integer'
		>0</owl:minCardinality>
	      </owl:Restriction>
	    </rdfs:subClassOf>
	    <rdfs:subClassOf>
	      <owl:Restriction>
		<owl:onProperty rdf:resource='{concat("#", "recurrenceId")}'/>
		<owl:minCardinality
		 rdf:datatype='http://www.w3.org/2001/XMLSchema#integer'
		>0</owl:minCardinality>
	      </owl:Restriction>
	    </rdfs:subClassOf>
	  </xsl:if>
	</xsl:for-each>

      </owl:Class>


      <!-- properties defined within a component,
	   e.g. standard and daylight -->
      <xsl:for-each select='.//h:a[@rel="def" and @id]'>
	<owl:ObjectProperty rdf:ID='{@id}'>
	  <rdfs:label><xsl:value-of select='normalize-space(.)'/></rdfs:label>
	</owl:ObjectProperty>
      </xsl:for-each>


      <!-- hmm... are these classes owl:disjoint? -->
    </xsl:for-each>


    <xsl:for-each select='h:div/h:dl[h:dd/@class="PropertyName"]'>
      <xsl:variable name='id' select='h:dt[@id]/@id'/>
      <xsl:variable name='purpose'
		    select='normalize-space(h:dd[@class="Purpose"])'/>
      <xsl:variable name='valueType'
		  select='h:dd/h:a[@rel="value-type"]'/>
      <xsl:variable name='defaultValueType'
		  select='h:dd/h:a[@rel="default-value-type"]'/>
      <xsl:variable name='valueListType'
		  select='h:dd/h:a[@rel="list-of"]'/>


      <xsl:variable name='name'
		  select='normalize-space(h:dd[@class="PropertyName"])'/>
      <xsl:variable name='label'>
	<xsl:choose>
	  <xsl:when test='$name = "DTSTART"'>
	    <xsl:text>start</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "DTEND"'>
	    <xsl:text>end</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "SUMMARY"'>
	    <xsl:text>summary</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "URL"'>
	    <xsl:text>see also</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "LOCATION"'>
	    <xsl:text>location</xsl:text>
	  </xsl:when>
	  <xsl:when test='$name = "ATTENDEE"'>
	    <xsl:text>attendee</xsl:text>
	  </xsl:when>
	</xsl:choose>
      </xsl:variable>
      <rdf:Description rdf:ID='{$id}'>
	<xsl:if test="string-length($label) &gt; 0">
	  <rdfs:label><xsl:value-of select='$label'/></rdfs:label>
	</xsl:if>
	<rdfs:comment><xsl:value-of select='$purpose'/></rdfs:comment>

	<!-- make summary a subPropertyOf rdfs:label -->
	<xsl:if test='contains($purpose, "summary or subject")'>
	  <rdfs:subPropertyOf
	      rdf:resource="http://www.w3.org/2000/01/rdf-schema#label" />
	</xsl:if>


	<xsl:if test="$valueType">
	  <rdfs:comment>
	    value type: <xsl:value-of select='$valueType'/>
	  </rdfs:comment>
	  <spec:valueType>
	    <xsl:value-of select='$valueType'/>
	  </spec:valueType>
	</xsl:if>
	<xsl:if test="$valueListType">
	  <rdfs:comment>
	    value type: list of <xsl:value-of select='$valueListType'/>
	  </rdfs:comment>
	  <spec:valueListType>
	    <xsl:value-of select='$valueListType'/>
	  </spec:valueListType>
	</xsl:if>
	<xsl:if test="$defaultValueType">
	  <rdfs:comment>
	    default value type: <xsl:value-of select='$defaultValueType'/>
	  </rdfs:comment>
	  <spec:valueType>
	    <xsl:value-of select='$defaultValueType'/>
	  </spec:valueType>
	</xsl:if>

	<xsl:choose>
	  <xsl:when test='$valueType = "INTEGER"
		    or $valueType = "integer"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#DatatypeProperty'/>
	    <rdfs:range>
	      <rdfs:Datatype
	       rdf:about='http://www.w3.org/2001/XMLSchema#integer'/>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$valueType = "DATE-TIME"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#DatatypeProperty'/>
	    <rdfs:range>
	      <rdfs:Datatype
	       rdf:about='#Value_DATE-TIME'/>
	    </rdfs:range>
	  </xsl:when>

	  <!-- perl-esque treatment of utc-offset types -->
	  <xsl:when test='$valueType = "TEXT"
		    or $valueType = "UTC-OFFSET"
		    '>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#DatatypeProperty'/>
	    <rdfs:range>
	      <rdfs:Datatype
	       rdf:about='http://www.w3.org/2001/XMLSchema#string'/>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$valueType = "DURATION"
		    or $valueType = "PERIOD"
		    '>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <rdfs:range>
	      <owl:Class rdf:about="{$valueType/@href}"/>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$defaultValueType = "DATE-TIME"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#DatatypeProperty'/>
	  </xsl:when>

	  <xsl:when test='$defaultValueType = "DURATION"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <rdfs:range>
	      <owl:Class>
		<owl:unionOf rdf:parseType="Collection">
		  <owl:Class rdf:about="#Value_{$defaultValueType}"/>

		  <!-- might be nice to remove dups -->
		  <xsl:for-each select='h:dd//h:a[@rel="allowed-type"]'>
		    <owl:Class rdf:about='{@href}'/>
		  </xsl:for-each>
		</owl:unionOf>
	      </owl:Class>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$valueType = "CAL-ADDRESS"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <rdfs:range>
	      <owl:Class rdf:about="#Value_CAL-ADDRESS"/>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$valueType = "RECUR"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <rdfs:range>
	      <owl:Class rdf:about="#Value_RECUR"/>
	    </rdfs:range>
	  </xsl:when>

	  <!-- hmm... is this use or mention of the URI? -->
	  <xsl:when test='$valueType = "URI"
		    or $defaultValueType = "URI"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <!-- range is unconstrained; a URI could refer to anything -->
	    <!-- or... hmm... constrain it to a Representation? -->
	  </xsl:when>

	  <xsl:when test='$valueListType = "FLOAT"'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <rdfs:range>
	      <!-- We could express the semantics of this class ala:
	        :FloatList subClassOf rdf:List,
	          [owl:onProperty rdf:first; owl:allValuesFrom :FloatLit ],
	          [owl:onProperty rdf:rest; owl:allValuesFrom :FloatList ].

		  I think "list of X" is discussed in the OWL specs
		  somewhere.
	      -->
	      <owl:Class rdf:about="#List_of_Float"/>
	    </rdfs:range>
	  </xsl:when>

	  <xsl:when test='$valueType or $defaultValueType'>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	    <xsl:message>
	      unrecognized range.
	      value type: <xsl:value-of select="$valueType"/>
	      default value type: <xsl:value-of select="$defaultValueType"/>
	    </xsl:message>
	  </xsl:when>

	  <xsl:otherwise>
	    <rdf:type rdf:resource=
		      'http://www.w3.org/2002/07/owl#ObjectProperty'/>
	  </xsl:otherwise>

	</xsl:choose>

	<!-- find components that this property can be specified on -->
	<xsl:variable name='domains' select='.//h:a[@rel="applies-to"]'/>
	<xsl:choose>
	  <!-- several of them... make a list -->
	  <xsl:when test='count($domains) &gt; 1'>
	    <rdfs:domain>
	      <owl:Class rdf:nodeID='{concat("DomainOf_", $id)}'>
		<owl:unionOf rdf:parseType="Collection">
		  <xsl:for-each select='$domains'>
		    <owl:Class rdf:about="{@href}"/>
		  </xsl:for-each>
		</owl:unionOf>
	      </owl:Class>
	    </rdfs:domain>
	  </xsl:when>

	  <!-- just one... -->
	  <xsl:when test='count($domains) = 1'>
	    <rdfs:domain rdf:resource='{$domains/@href}'/>
	  </xsl:when>

	  <xsl:when test='contains(h:dd[@class="Conformance"],
		    "includes a recurring calendar component") or
		    contains(h:dd[@class="Conformance"],
		    "containing a recurring calendar component")'>
	    <rdfs:domain>
	      <owl:Class rdf:about='#DomainOf_rrule'/>
	    </rdfs:domain>
	  </xsl:when>

	  <xsl:otherwise>
	    <!-- pass -->
	  </xsl:otherwise>
	</xsl:choose>
      </rdf:Description>

    </xsl:for-each>

    <xsl:for-each select='h:div/h:dl[h:dd/@class="ValueName"]'>
      <!-- parameters -->
      <xsl:for-each select='.//h:a[@rel="def" and @id]'>
	<owl:DatatypeProperty rdf:ID='{@id}'>
	  <rdfs:label><xsl:value-of select='normalize-space(.)'/></rdfs:label>
	</owl:DatatypeProperty>
      </xsl:for-each>
    </xsl:for-each>

    <xsl:for-each select='h:div/h:dl[h:dd/@class="ParameterName"]'>
      <xsl:variable name='id' select='h:dt[@id]/@id'/>
      <xsl:if test='$id != "value"'> <!-- mapped to rdf datatypes -->
	<xsl:variable name='name'
		      select='normalize-space(h:dd[@class="ParameterName"])'/>
	<xsl:variable name='purpose'
		      select='normalize-space(h:dd[@class="Purpose"])'/>
	<owl:DatatypeProperty rdf:ID='{$id}'>
	  <rdfs:comment><xsl:value-of select='$purpose'/></rdfs:comment>
	  
	</owl:DatatypeProperty>
      </xsl:if>
    </xsl:for-each>


    <!-- sorta hard-coded... -->
    <xsl:for-each select='h:div[contains(h:h2, "4.4 iCalendar Object")]'>
      <owl:Class rdf:ID="Vcalendar">
	<rdfs:label>VCALENDAR</rdfs:label>
      </owl:Class>
      <owl:ObjectProperty rdf:ID="component">
	<!-- hmm... domain is bigger than just Vcalendar, isn't it? -->
      </owl:ObjectProperty>

      <rdfs:Datatype rdf:about="#dateTime" />
      <owl:ObjectProperty rdf:about="#calAddress" />

      <owl:DatatypeProperty rdf:about="http://www.w3.org/1999/02/22-rdf-syntax-ns#value">
	<!-- also... Value_DURATION onProperty rdf:value;
	     allValuesFrom xsdt:duration.
	     but that's outside DL too.
	-->
      </owl:DatatypeProperty>
    </xsl:for-each>

  </rdf:RDF>

</xsl:template>


<!-- don't pass text thru -->
<xsl:template match="text()|@*">
</xsl:template>


</xsl:stylesheet>
