← all demos
xsl:stylesheet(
    exclude-result-prefixes="dbe f fp m map v vp xs",
    version="3.0",
    xmlns:dbe="http://docbook.org/ns/docbook/errors",
    xmlns:f="http://docbook.org/ns/docbook/functions",
    xmlns:fp="http://docbook.org/ns/docbook/functions/private",
    xmlns:m="http://docbook.org/ns/docbook/modes",
    xmlns:map="http://www.w3.org/2005/xpath-functions/map",
    xmlns:v="http://docbook.org/ns/docbook/variables",
    xmlns:vp="http://docbook.org/ns/docbook/variables/private",
    xmlns:xs="http://www.w3.org/2001/XMLSchema",
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform")
  xsl:include(href="../environment.xsl")
  vp:strmatch := '^(\$\P{Zs}+)\s*=\s*(.+)$'
  vp:varmatch := '^(\$\P{Zs}+)$'
  vp:profile-variables as map(*) := map:merge(($vp:dynamic-parameters, $dynamic-profile-variables))
  vp:profile-value-map as map(xs:QName, xs:string*) :=
    xsl:map
      xsl:map-entry(key="xs:QName('xml:lang')", select="fp:profile-tokens($profile-lang)")
      xsl:map-entry(
          key="QName('','revisionflag')",
          select="fp:profile-tokens($profile-revisionflag)")
      xsl:map-entry(key="QName('','role')", select="fp:profile-tokens($profile-role)")
      xsl:map-entry(key="QName('','arch')", select="fp:profile-tokens($profile-arch)")
      xsl:map-entry(key="QName('','audience')", select="fp:profile-tokens($profile-audience)")
      xsl:map-entry(key="QName('','condition')", select="fp:profile-tokens($profile-condition)")
      xsl:map-entry(key="QName('','conformance')", select="fp:profile-tokens($profile-conformance)")
      xsl:map-entry(key="QName('','os')", select="fp:profile-tokens($profile-os)")
      xsl:map-entry(
          key="QName('','outputformat')",
          select="fp:profile-tokens($profile-outputformat)")
      xsl:map-entry(key="QName('','revision')", select="fp:profile-tokens($profile-revision)")
      xsl:map-entry(key="QName('','security')", select="fp:profile-tokens($profile-security)")
      xsl:map-entry(key="QName('','userlevel')", select="fp:profile-tokens($profile-userlevel)")
      xsl:map-entry(key="QName('','vendor')", select="fp:profile-tokens($profile-vendor)")
      xsl:map-entry(key="QName('','wordsize')", select="fp:profile-tokens($profile-wordsize)")
  vp:profile-attributes := map:keys($vp:profile-value-map)
  match / -> document-node():
    has-profile as string* :=
      foreach map:keys($vp:profile-value-map):
        <-- map:get($vp:profile-value-map, .)
    choose:
      when not(f:is-true($dynamic-profiles)) and empty($has-profile):
        <-- .
      else:
        xsl:document
          apply
  match *:
    choose:
      when fp:profile-suppress(.):
      else:
        copy:
          apply @*,node()
  match attribute()|text()|comment()|processing-instruction():
    copy
  function fp:profile-tokens(profile as string*) -> string*:
    foreach tokenize($profile, $profile-separator):
      <-- if (normalize-space(.) = ''
                              or matches(., $vp:strmatch)
                              or matches(., $vp:varmatch))
                          then ()
                          else normalize-space(.)
  function fp:dynamic-profile-tokens(profile as string*) -> string*:
    foreach tokenize($profile, $profile-separator):
      <-- if (matches(., $vp:strmatch)
                              or matches(., $vp:varmatch))
                          then .
                          else ()
  function fp:profile-suppress(context as element()) -> boolean:
    profile as map(xs:QName, item()+) :=
      xsl:map
        foreach $context/@*[node-name(.) = $vp:profile-attributes]:
          name := node-name(.)
          value := map:get($vp:profile-value-map, $name)
          if f:is-true($dynamic-profiles) or exists($value):
            xsl:map-entry(key="$name", select="(., $value)")
    choose:
      when map:size($profile) = 0:
        <-- false()
      else:
        suppress as QName* :=
          foreach map:keys($profile):
            value := string(map:get($profile, .)[1])
            tokens := subsequence(map:get($profile, .), 2)
            ptokens := fp:profile-tokens($value)
            if exists($tokens) and exists($ptokens)
                        and not($tokens = $ptokens):
              xsl:message(use-when="'profile-suppress' = $v:debug")
                "Suppressed"
                <-- if ($context/@xml:id)
                                    then node-name($context) || '/' || $context/@xml:id
                                    else node-name($context)
                <-- ':', $tokens, '!=', fp:profile-tokens($value)
              <-- .
        choose:
          when exists($suppress):
            <-- true()
          when not(f:is-true($dynamic-profiles)):
            <-- false()
          else:
            include as boolean* :=
              foreach map:keys($profile):
                value := map:get($profile, .)[1]
                tokens := fp:dynamic-profile-tokens(string($value))
                foreach $tokens:
                  if false() = fp:dynamic-include($context, .):
                    xsl:message
                      "Exclude"
                      <-- if ($context/@xml:id)
                                then node-name($context) || '/' || $context/@xml:id
                                else node-name($context)
                      <-- ':', .
                  if not(false() = fp:dynamic-include($context, .)):
                    xsl:message
                      "Include"
                      <-- if ($context/@xml:id)
                                then node-name($context) || '/' || $context/@xml:id
                                else node-name($context)
                      <-- ':', .
                  <-- fp:dynamic-include($context, .)
            <-- false() = $include
  function fp:dynamic-include(context as element(), expr as string) -> boolean?:
    choose:
      when matches($expr, $vp:strmatch):
        var := replace($expr, $vp:strmatch, '$1')
        value := replace($expr, $vp:strmatch, '$2')
        <-- fp:check-profile($context, $var, $value)
      when matches($expr, $vp:varmatch):
        <-- fp:check-profile($context, $expr, ())
      else:
        <-- error($dbe:DYNAMIC-PROFILE-SYNTAX-ERROR,
                                  'Unparseable: ' || $expr)
  function fp:check-profile -> boolean?:
    param context as element()
    param variable as string
    param expected-value as string?
    expected-value := if ((starts-with($expected-value, '''')
                   and ends-with($expected-value, ''''))
                  or
                  (starts-with($expected-value, '"')
                   and ends-with($expected-value, '"')))
              then substring($expected-value, 2, string-length($expected-value) - 2)
              else $expected-value
    xsl:try
      actual-value as item()* :=
        xsl:evaluate(
            context-item="$context",
            namespace-context="$context",
            with-params="$vp:profile-variables",
            xpath="$variable")
      choose:
        when exists($expected-value):
          <-- $expected-value = ($actual-value ! string(.))
        when ('no','false','0') = ($actual-value ! string(.)):
          <-- false()
        when ('yes','true','1') = ($actual-value ! string(.)):
          <-- true()
        else:
          <-- true() = ($actual-value ! boolean(.))
      xsl:catch(xmlns:err="http://www.w3.org/2005/xqt-errors")
        xsl:message(select="$err:code, $err:description")
        choose:
          when $dynamic-profile-error = 'ignore':
            <-- ()
          when $dynamic-profile-error = 'include':
            <-- true()
          when $dynamic-profile-error = 'exclude':
            <-- false()
          when $dynamic-profile-error = 'error':
            <-- error($dbe:DYNAMIC-PROFILE-EVAL-ERROR,
                                'Dynamic profiling error: ' || $variable)
          else:
            <-- error($dbe:INVALID-DYNAMIC-PROFILE-ERROR,
                      'Invalid $dynamic-profile-error: ' || $dynamic-profile-error)