view refs.xsl @ 34:93fd0d532754

fix bug in refs wrt e.g. [1]!.SGX, adapt html and ascii to new-format refs, move a2n and n2a into separate files for re-use
author Henry S. Thompson <ht@markup.co.uk>
date Wed, 12 Apr 2017 21:35:04 +0100
parents 27bffc66ce10
children ae605b77d1e4
line wrap: on
line source

<?xml version='1.0'?>
<!DOCTYPE doc SYSTEM "../../../lib/xml/xsl.dtd" >
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:s="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:e="http://markup.co.uk/excel" exclude-result-prefixes="xs s e xf" xmlns="http://markup.co.uk/excel" xmlns:xf="http://www.w3.org/2005/xpath-functions">
 <xsl:param name="sheet-number"/>
 <xsl:param name="xlDir"/>

  <xsl:variable name="pat1">("[^"]*")|(\{[^}]+})|(,)|([^=\-+*/();:,.$&lt;>^!]+(?:\.[^=\-+*/();:,.$&lt;>^!]+)*\()|([)])|(^=|\()|((?:'[^']+')|(?:\[[0-9]+\][^!]*))|(\$?[A-Z]+\$?[0-9]+)|([a-zA-Z_\\][a-zA-Z0-9._]*)|(.)</xsl:variable>
 <xsl:param name="pat" select="$pat1"/><!-- xsl:param for refinement debugging by passing in the pattern -->
 
 <xsl:variable name="workbook" select="document(concat($xlDir,'/workbook.xml'))/*"/>
 <xsl:variable name="sheet-name" select="$workbook/s:sheets/s:sheet[@sheetId=$sheet-number]/@name"/>
 
 <xsl:function name="e:lookup" as="xs:string*">
  <xsl:param name="name" as="xs:string" required="yes"/>
  <xsl:variable name="defn" select="$workbook/s:definedNames/s:definedName[@name=$name]"/>
  <xsl:sequence select="let $prefix := concat($sheet-name,'!')
                   return if ($defn and
                              starts-with($defn,$prefix))
                           then substring-after($defn,$prefix)
                           else ()"/>
 </xsl:function>

 <xsl:function name="e:tokenise" as="array(element(*)*)*">
  <!-- Tokenise a formula, recursively wrt variables -->
  <xsl:param name="formula" as="xs:string" required="yes"/>
  <xsl:sequence select="
    let $tokens := analyze-string($formula,$pat)/xf:match/xf:group
     return if ($tokens[@nr=(7,8,9)])
             then 
              let $n := count($tokens),
                  $vars := for $i in (1 to $n) return
                         let $t := $tokens[$i],
                             $l := $tokens[$i - 1],
                             $r := $tokens[$i + 1] return    
                          if ($t/@nr=9 and
                              not($l[@nr=10 and
                                     .=(':','!')]) and
                              not($r[@nr=10 and .=':']))
                            then string($t)
                            else (),
                  $defns := for $var in $vars return e:lookup($var),
                  $recur := for $sub in $defns 
                              return if ($sub) then e:tokenise($sub) else (),
                  $singles := for $i in (1 to $n) return
                            let $t := $tokens[$i],
                                $l := $tokens[$i - 1],
                                $r := $tokens[$i + 1] return
                            if ($t/@nr=8 and
                                not($l[@nr=10 and
                                       .=(':','!')]) and
                                not($r[@nr=10 and .=':']))
                             then e:single($t,false())
                             else (),
                  $ranges := for $i in (1 to count($tokens)) return
                            let $t := $tokens[$i] return
                            if ($t[@nr=10 and .=':' and
                                   not($i gt 2 and
                                       $tokens[$i - 2][@nr=10 and .='!'])])
                             then let $l := $tokens[$i - 1],
                                      $r := $tokens[$i + 1]
                                      return e:range(e:single($l,false()),
                                                     e:single($r,false()))
                             else (),
                  $externals := for $i in (1 to count($tokens)) return
                            let $t := $tokens[$i] return
                            if ($t/@nr=7 and $tokens[$i+1]='!')
                             then 
                              let $ext := $t!='[0]',
                                  $ref := e:single($tokens[$i + 2],
                                                   $ext),
                                  $res := if ((($i+3) le $n) and
                                              $tokens[$i + 3][@nr=10 and .=':'])
                                           then e:range($ref,
                                                        e:single($tokens[$i+4],
                                                                 $ext))
                                           else $ref return
                              if ($ext)
                               then e:external($t,$res)
                               else $res
                             else ()
                  return [($singles,for $a in $recur return $a?1),
                          ($ranges,for $a in $recur return $a?2),
                          ($externals,for $a in $recur return $a?3)]
             else ()"/>
 </xsl:function>
 
 <xsl:function name="e:single" as="element(*)">
  <xsl:param name="group" as="element(xf:group)"/>
  <xsl:param name="external" as="xs:boolean"/>
  <xsl:variable name="val" select="if ($group/@nr=9) then e:lookup($group)
                                             else string($group)"/>
  <xsl:choose>
   <xsl:when test="count($val)>0 or not($external)">
    <s><xsl:value-of select="$val"/></s>
   </xsl:when>
   <xsl:otherwise>
    <v><xsl:value-of select="$group"/></v>
   </xsl:otherwise>
  </xsl:choose>
  
 </xsl:function>
 
 <xsl:function name="e:range" as="element(e:r)">
  <xsl:param name="l" as="element(e:s)" required="yes"/>
  <xsl:param name="r" as="element(e:s)" required="yes"/>
  <r><xsl:copy-of select="$l"/><xsl:copy-of select="$r"/></r>
 </xsl:function>
 
 <xsl:function name="e:external" as="element(e:e)">
  <xsl:param name="source" as="element(xf:group)" required="yes"/>
  <xsl:param name="ref" as="element(*)" required="yes"/>
  <e s="{$source}"><xsl:sequence select="$ref"/></e>
 </xsl:function>

 <xsl:template match="/">
  <refs sheetName="{$sheet-name}"><xsl:apply-templates select="//s:c"/></refs>
 </xsl:template>
 
 <xsl:template match="s:c[s:f]">
  <xsl:variable name="tokens" select="e:tokenise(s:f/.)"/>
  <xsl:if test="@r='xxx'"><xsl:message><xsl:value-of select="s:f"/>|<xsl:value-of select="(analyze-string(s:f/.,$pat)/xf:match/xf:group)[3]/@nr"/></xsl:message>
  </xsl:if>
  <xsl:if test="count($tokens)>0">
   <xsl:variable name="singles" select="$tokens?1"/>
   <!-- Note that we don't bother to treat external ranges as ranges,
          since we're not going to try to detect cross-document refs -->
   <xsl:variable name="ranges" select="$tokens?2"/>
   <xsl:variable name="externals" select="$tokens?3"/>
   <!-- Lost distinct-values filter, not sure it's really possible... -->
   <ref c="{@r}">
    <xsl:copy-of select="$singles"/>
    <xsl:copy-of select="$ranges"/>
    <xsl:copy-of select="$externals"/>
   </ref></xsl:if>
 </xsl:template>
 
 <xsl:template match="s:c"/>
</xsl:stylesheet>