Mercurial > hg > ooxml
view refs.xsl @ 29:87ed04a0fde2
recursion in place, need to check for names in ranges
author | Henry S. Thompson <ht@markup.co.uk> |
---|---|
date | Tue, 11 Apr 2017 14:33:14 +0100 |
parents | c56a2e6990bd |
children | 16eff0d30d4d |
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">("[^"]*")|(\{[^}]+})|(,)|([^=\-+*/();:,.$<>^!]+(?:\.[^=\-+*/();:,.$<>^!]+)*\()|([)])|(^=|\()|((?:'[^']+')|(?:\[[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(xs:string*)*"> <!-- 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 if ($tokens[$i][@nr=9] and not($tokens[$i - 1][@nr=10 and .=(':','!')]) and not($tokens[$i + 1][@nr=10 and .=':'])) then string($tokens[$i]) else (), $defns := for $var in $vars return e:lookup($var), $recur := for $sub in $defns return if ($sub) then e:tokenise($defns) else (), $singles := for $i in (1 to $n) return if ($tokens[$i][@nr=8] and not($tokens[$i - 1][@nr=10 and .=(':','!')]) and not($tokens[$i + 1][@nr=10 and .=':'])) then translate($tokens[$i],'$','') else (), $ranges := for $i in (1 to count($tokens)) return if ($tokens[$i][@nr=10 and .=':' and not($i gt 2 and $tokens[$i - 2][@nr=10 and .='!'])]) then translate(concat($tokens[$i - 1],':', $tokens[$i + 1]),'$','') else (), $externals := for $i in (1 to count($tokens)) return if ($tokens[$i][@nr=7]) then let $bit := concat($tokens[$i],'!', translate($tokens[$i + 2], '$','')) return if ((($i+3) le $n) and $tokens[$i + 3][@nr=10 and .=':']) then concat($bit,':', translate($tokens[$i + 4], '$','')) else $bit 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: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: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"/> <ref c="{@r}"> <xsl:for-each select="distinct-values($singles)"> <s><xsl:value-of select="."/></s> </xsl:for-each> <xsl:for-each select="distinct-values($ranges)"> <r><xsl:value-of select="."/></r> </xsl:for-each> <xsl:for-each select="distinct-values($externals)"> <e><xsl:value-of select="."/></e> </xsl:for-each> </ref></xsl:if> </xsl:template> <xsl:template match="s:c"/> </xsl:stylesheet>