Difference between revisions of "InsertFalsePrecision1.xsl"

From TEIWiki
Jump to navigation Jump to search
m (put in XSLT category)
m (Add to XSLT:1.0; improve indentation)
Line 12: Line 12:
  
 
   Known limitations:
 
   Known limitations:
* Only handles times, not dateTimes. The XSLT 2 version
+
  * Only handles times, not dateTimes. The XSLT 2 version
  (add_false_time_precision_4v2.xslt) handles both.
+
    (add_false_time_precision_4v2.xslt) handles both.
* The "00" should probably be a parameter, so users could use some
+
  * The "00" should probably be a parameter, so users could use some
other value (e.g., "30") if they wanted.
+
    other value (e.g., "30") if they wanted.
* Obviously, the file cannot be converted back, as there would be no
+
  * Obviously, the file cannot be converted back, as there would be no
way for a stylesheet to know whether any particular ":00" was
+
    way for a stylesheet to know whether any particular ":00" was
added by this stylesheet or actually represents a time precise to
+
    added by this stylesheet or actually represents a time precise to
the minute or second.
+
    the minute or second.
  
 
   I don't claim for a moment that the method I've used here is the
 
   I don't claim for a moment that the method I've used here is the
Line 33: Line 33:
 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  
<!-- Generic copy-everything template -->                                                                                 
+
  <!-- Generic copy-everything template -->                                                                                 
<xsl:template match="@*|*|processing-instruction()|comment()">                                                             
+
  <xsl:template match="@*|*|processing-instruction()|comment()">                                                             
<xsl:copy>                                                                                                               
+
    <xsl:copy>                                                                                                               
<xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()"/>                                         
+
      <xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()"/>                                         
</xsl:copy>                                                                                                             
+
    </xsl:copy>                                                                                                             
</xsl:template>                                                                                                           
+
  </xsl:template>                                                                                                           
+
 
<xsl:template match="time[@value]|tei:time[@value]|date[@value]|tei:date[@value]">
+
  <xsl:template match="time[@value]|tei:time[@value]|date[@value]|tei:date[@value]">
 
     <!-- strip any leading or trailing whitespacce -->
 
     <!-- strip any leading or trailing whitespacce -->
<xsl:variable name="value">
+
    <xsl:variable name="value">
<xsl:value-of select="normalize-space(@value)"/>
+
      <xsl:value-of select="normalize-space(@value)"/>
</xsl:variable>
+
    </xsl:variable>
<!-- First, ascertain whether or not there is a time zone designation, -->
+
    <!-- First, ascertain whether or not there is a time zone designation, -->
<!-- and if so, remember whether we have the UTC indicator ("Z"), or  -->
+
    <!-- and if so, remember whether we have the UTC indicator ("Z"), or  -->
<!-- are ahead of or at UTC ("+hh:mm") or behind UTC ("-hh:mm").      -->
+
    <!-- are ahead of or at UTC ("+hh:mm") or behind UTC ("-hh:mm").      -->
<xsl:variable name="zoneSep">
+
    <xsl:variable name="zoneSep">
<!-- Slightly misnamed variable, as the Z is the entire zone  -->
+
      <!-- Slightly misnamed variable, as the Z is the entire zone  -->
<!-- designator, and the + or - is really the first character  -->
+
      <!-- designator, and the + or - is really the first character  -->
<!-- of the zone designator, not a separator. Nonetheless,    -->
+
      <!-- of the zone designator, not a separator. Nonetheless,    -->
<!-- set zoneSep to nothing, "Z", "-", or "+", depending on    -->
+
      <!-- set zoneSep to nothing, "Z", "-", or "+", depending on    -->
<!-- the first character of the zone designator. Here we rely  -->
+
      <!-- the first character of the zone designator. Here we rely  -->
<!-- on the fact that the characters +, -, and Z cannot appear -->
+
      <!-- on the fact that the characters +, -, and Z cannot appear -->
<!-- anywhere else in an ISO date or time. -->
+
      <!-- anywhere else in an ISO date or time. -->
<xsl:choose>
+
      <xsl:choose>
<xsl:when test="contains($value,'-')">
+
        <xsl:when test="contains($value,'-')">
<xsl:text>-</xsl:text>
+
          <xsl:text>-</xsl:text>
</xsl:when>
+
        </xsl:when>
<xsl:when test="contains($value,'+')">
+
        <xsl:when test="contains($value,'+')">
<xsl:text>+</xsl:text>
+
          <xsl:text>+</xsl:text>
</xsl:when>
+
        </xsl:when>
<xsl:when test="contains($value,'Z')">
+
        <xsl:when test="contains($value,'Z')">
<!-- It will always be the last character, but AFAIK there -->
+
          <!-- It will always be the last character, but AFAIK there -->
<!-- is no ends-with() function in XSLT 1.0. -->
+
          <!-- is no ends-with() function in XSLT 1.0. -->
<xsl:text>Z</xsl:text>
+
          <xsl:text>Z</xsl:text>
</xsl:when>
+
        </xsl:when>
<xsl:otherwise>
+
        <xsl:otherwise>
<xsl:text/>
+
          <xsl:text/>
<!-- Is there a better way to set to null? -->
+
          <!-- Is there a better way to set to null? -->
</xsl:otherwise>
+
        </xsl:otherwise>
</xsl:choose>
+
      </xsl:choose>
</xsl:variable>
+
    </xsl:variable>
<!-- Parse out the time indication from the zone designator. -->
+
    <!-- Parse out the time indication from the zone designator. -->
<xsl:variable name="time">
+
    <xsl:variable name="time">
<xsl:choose>
+
      <xsl:choose>
<xsl:when test="$zoneSep=''">
+
        <xsl:when test="$zoneSep=''">
<!-- No zone designator, so time is entire value -->
+
          <!-- No zone designator, so time is entire value -->
<xsl:value-of select="$value"/>
+
          <xsl:value-of select="$value"/>
</xsl:when>
+
        </xsl:when>
<xsl:otherwise>
+
        <xsl:otherwise>
<!-- There is a zone designator, time is whatever is -->
+
          <!-- There is a zone designator, time is whatever is -->
<!-- before it. -->
+
          <!-- before it. -->
<xsl:value-of select="substring-before($value,$zoneSep)"/>
+
          <xsl:value-of select="substring-before($value,$zoneSep)"/>
</xsl:otherwise>
+
        </xsl:otherwise>
</xsl:choose>
+
      </xsl:choose>
</xsl:variable>
+
    </xsl:variable>
<xsl:variable name="zone">
+
    <xsl:variable name="zone">
<xsl:choose>
+
      <xsl:choose>
<xsl:when test="$zoneSep=''">
+
        <xsl:when test="$zoneSep=''">
<!-- No zone designator -->
+
          <!-- No zone designator -->
<xsl:text/>
+
          <xsl:text/>
</xsl:when>
+
        </xsl:when>
<xsl:otherwise>
+
        <xsl:otherwise>
<!-- There is a zone designator, remember whatever is after its -->
+
          <!-- There is a zone designator, remember whatever is after its -->
<!-- first character. -->
+
          <!-- first character. -->
<xsl:value-of select="substring-after($value,$zoneSep)"/>
+
          <xsl:value-of select="substring-after($value,$zoneSep)"/>
</xsl:otherwise>
+
        </xsl:otherwise>
</xsl:choose>
+
      </xsl:choose>
</xsl:variable>
+
    </xsl:variable>
<!-- Now see if we need to give time some false precision -->
+
    <!-- Now see if we need to give time some false precision -->
<xsl:variable name="newValue">
+
    <xsl:variable name="newValue">
<!-- Assign new value based on current time format -->
+
      <!-- Assign new value based on current time format -->
<xsl:choose>
+
      <xsl:choose>
<xsl:when test="string-length($time)=2">
+
        <xsl:when test="string-length($time)=2">
<!-- must be "hh" -->
+
          <!-- must be "hh" -->
<xsl:value-of select="$time"/>
+
          <xsl:value-of select="$time"/>
<xsl:text>:00:00</xsl:text>
+
          <xsl:text>:00:00</xsl:text>
<xsl:value-of select="$zoneSep"/>
+
          <xsl:value-of select="$zoneSep"/>
<xsl:value-of select="$zone"/>
+
          <xsl:value-of select="$zone"/>
</xsl:when>
+
        </xsl:when>
<xsl:when test="string-length($time)=5">
+
        <xsl:when test="string-length($time)=5">
<!-- must be "hh:mm" -->
+
          <!-- must be "hh:mm" -->
<xsl:value-of select="$time"/>
+
          <xsl:value-of select="$time"/>
<xsl:text>:00</xsl:text>
+
          <xsl:text>:00</xsl:text>
<xsl:value-of select="$zoneSep"/>
+
          <xsl:value-of select="$zoneSep"/>
<xsl:value-of select="$zone"/>
+
          <xsl:value-of select="$zone"/>
</xsl:when>
+
        </xsl:when>
<xsl:otherwise>
+
        <xsl:otherwise>
<!-- must be "hh:mm:ss[.s*]" -->
+
          <!-- must be "hh:mm:ss[.s*]" -->
<xsl:value-of select="$value"/>
+
          <xsl:value-of select="$value"/>
</xsl:otherwise>
+
        </xsl:otherwise>
</xsl:choose>
+
      </xsl:choose>
</xsl:variable>
+
    </xsl:variable>
<!-- OK, now spit out a copy of this element that's the same, -->
+
    <!-- OK, now spit out a copy of this element that's the same, -->
<!-- but uses the new value -->
+
    <!-- but uses the new value -->
<xsl:element name="name(.)">
+
    <xsl:element name="name(.)">
<!-- create <time> element, with all the original attributes ... -->
+
      <!-- create <time> element, with all the original attributes ... -->
<xsl:copy-of select="@*"/>
+
      <xsl:copy-of select="@*"/>
<!-- ... but then copy over a new value= attribute, using -->
+
      <!-- ... but then copy over a new value= attribute, using -->
<xsl:attribute name="value">
+
      <xsl:attribute name="value">
<!-- ... our new value for it ... -->
+
        <!-- ... our new value for it ... -->
<xsl:value-of select="$newValue"/>
+
        <xsl:value-of select="$newValue"/>
<!-- ... replacing the original value= -->  
+
        <!-- ... replacing the original value= -->  
</xsl:attribute>
+
      </xsl:attribute>
<xsl:apply-templates/>
+
      <xsl:apply-templates/>
<!-- ... and all text and element children -->
+
      <!-- ... and all text and element children -->
</xsl:element>
+
    </xsl:element>
</xsl:template>
+
  </xsl:template>
  
 
</xsl:stylesheet>
 
</xsl:stylesheet>
</pre>[[Category:XSLT]]
+
</pre>
 +
[[Category:XSLT]][[Category:XSLT:1.0]]

Revision as of 01:32, 2 October 2005

TEI datatypes permit times on the value= attribute of the various date & time elements to be expressed to any precision (year, month, day, hour, minute, second, or decimal fraction of a second). However, because the W3C XML Schema datatypes for times do not permit precision to the hour or minute, there exists software that does not handle these values. E.g., such software will reject "13:30" but accept "13:30:00". This styleseet (as well as the better version that only works with XSLT 2 processors) converts times that are precise to only the minute or hour to times that are precise to the second.

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
  add_false_time_precision_4v1.xslt

  This style-sheet reads in an XML file (presumably a TEI P5 one, but
  any other that has similar value= attributes of <date> and <time>
  elements would work) and writes out the same file with any right-
  truncated times padded with zeroes.

  Known limitations:
  * Only handles times, not dateTimes. The XSLT 2 version
    (add_false_time_precision_4v2.xslt) handles both.
  * The "00" should probably be a parameter, so users could use some
    other value (e.g., "30") if they wanted.
  * Obviously, the file cannot be converted back, as there would be no
    way for a stylesheet to know whether any particular ":00" was
    added by this stylesheet or actually represents a time precise to
    the minute or second.

  I don't claim for a moment that the method I've used here is the
  best in any sense of the word "best", only that it is the one this
  novice XSLTer thought of. In particular, the nesting of setting the
  timezone separator and parsing out the time from the timezone might
  be better if it were in some other order, and likely could benefit
  from abstracting out a separate template.
  
  Written 2005-09-25 by Syd Bauman
  copyleft 2005 by Syd Bauman and the Text Encoding Initiative Consortium
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <!-- Generic copy-everything template -->                                                                                 
  <xsl:template match="@*|*|processing-instruction()|comment()">                                                            
    <xsl:copy>                                                                                                              
      <xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()"/>                                        
    </xsl:copy>                                                                                                             
  </xsl:template>                                                                                                           
  
  <xsl:template match="time[@value]|tei:time[@value]|date[@value]|tei:date[@value]">
    <!-- strip any leading or trailing whitespacce -->
    <xsl:variable name="value">
      <xsl:value-of select="normalize-space(@value)"/>
    </xsl:variable>
    <!-- First, ascertain whether or not there is a time zone designation, -->
    <!-- and if so, remember whether we have the UTC indicator ("Z"), or   -->
    <!-- are ahead of or at UTC ("+hh:mm") or behind UTC ("-hh:mm").       -->
    <xsl:variable name="zoneSep">
      <!-- Slightly misnamed variable, as the Z is the entire zone   -->
      <!-- designator, and the + or - is really the first character  -->
      <!-- of the zone designator, not a separator. Nonetheless,     -->
      <!-- set zoneSep to nothing, "Z", "-", or "+", depending on    -->
      <!-- the first character of the zone designator. Here we rely  -->
      <!-- on the fact that the characters +, -, and Z cannot appear -->
      <!-- anywhere else in an ISO date or time. -->
      <xsl:choose>
        <xsl:when test="contains($value,'-')">
          <xsl:text>-</xsl:text>
        </xsl:when>
        <xsl:when test="contains($value,'+')">
          <xsl:text>+</xsl:text>
        </xsl:when>
        <xsl:when test="contains($value,'Z')">
          <!-- It will always be the last character, but AFAIK there -->
          <!-- is no ends-with() function in XSLT 1.0. -->
          <xsl:text>Z</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text/>
          <!-- Is there a better way to set to null? -->
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- Parse out the time indication from the zone designator. -->
    <xsl:variable name="time">
      <xsl:choose>
        <xsl:when test="$zoneSep=''">
          <!-- No zone designator, so time is entire value -->
          <xsl:value-of select="$value"/>
        </xsl:when>
        <xsl:otherwise>
          <!-- There is a zone designator, time is whatever is -->
          <!-- before it. -->
          <xsl:value-of select="substring-before($value,$zoneSep)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="zone">
      <xsl:choose>
        <xsl:when test="$zoneSep=''">
          <!-- No zone designator -->
          <xsl:text/>
        </xsl:when>
        <xsl:otherwise>
          <!-- There is a zone designator, remember whatever is after its -->
          <!-- first character. -->
          <xsl:value-of select="substring-after($value,$zoneSep)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- Now see if we need to give time some false precision -->
    <xsl:variable name="newValue">
      <!-- Assign new value based on current time format -->
      <xsl:choose>
        <xsl:when test="string-length($time)=2">
          <!-- must be "hh" -->
          <xsl:value-of select="$time"/>
          <xsl:text>:00:00</xsl:text>
          <xsl:value-of select="$zoneSep"/>
          <xsl:value-of select="$zone"/>
        </xsl:when>
        <xsl:when test="string-length($time)=5">
          <!-- must be "hh:mm" -->
          <xsl:value-of select="$time"/>
          <xsl:text>:00</xsl:text>
          <xsl:value-of select="$zoneSep"/>
          <xsl:value-of select="$zone"/>
        </xsl:when>
        <xsl:otherwise>
          <!-- must be "hh:mm:ss[.s*]" -->
          <xsl:value-of select="$value"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- OK, now spit out a copy of this element that's the same, -->
    <!-- but uses the new value -->
    <xsl:element name="name(.)">
      <!-- create <time> element, with all the original attributes ... -->
      <xsl:copy-of select="@*"/>
      <!-- ... but then copy over a new value= attribute, using -->
      <xsl:attribute name="value">
        <!-- ... our new value for it ... -->
        <xsl:value-of select="$newValue"/>
        <!-- ... replacing the original value= --> 
      </xsl:attribute>
      <xsl:apply-templates/>
      <!-- ... and all text and element children -->
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>