<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/pool/xslt_ht.xslt" type="application/xml"?>
<xsl:stylesheet
  xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
  xmlns:d = "http://herbaer.de/xmlns/20051201/doc"
  xmlns = "http://www.w3.org/1999/xhtml"
  exclude-result-prefixes = "d"
  version = "1.0"
>
<d:info xmlns="http://herbaer.de/xmlns/20051201/doc">
  <title>list.xslt</title>
  <subtitle>Vorlagen zur Arbeit mit Listen</subtitle>
  <date>2011-01-21</date>
  <author>
    <personname>
      <firstname>Herbert</firstname>
      <surname>Schiemann</surname>
    </personname>
    <email>h.schiemann@herbaer.de</email>
  </author>
</d:info>

<d:section xmlns="http://herbaer.de/xmlns/20051201/doc" role="stylesheet">
<para>
Diese Datei enthält benannte Hilfsvorlagen, die die Einträge einer Liste
akkumulieren, zu einer neuen Liste bearbeiten, auf zwei Listen aufteilen
oder sortieren.
Sie nutzt Vorlage aus <d:filename>txt.xslt</d:filename>.
Wenn ein Stylesheet diese Datei (<d:filename>list.xslt</d:filename>) einbindet,
sollte es daher auch <d:filename>txt.xslt</d:filename> einbinden.
</para>
<para>
Eine Liste ist eine Zeichenkette,
die durch eine feste Zeichenfolge (die Trennzeichenfolge)
in einzelne Listeneinträge zerlegt wird.
Einige Vorlagen behandeln die leere Zeichenkette als Listeneintrag so,
als gäbe es diesen Eintrag nicht.
Ein Listeneintrag sollte also, so wie Listen hier behandelt werden, nicht leer sein.
</para>
<para>
Die Trennzeichenfolge kann jeder Vorlage für jede Liste als Parameter übergeben werden.
Wenn eine Vorlage mit mehreren Listen arbeitet,
können die Listen verschiedene Trennzeichenfolgen verwenden.
Voreingestellt ist ein einzelnes Leerzeichen.
</para>
<para>
Die Listeneinträge werden von 1 an gezählt (Position eines Listeneintrags).
</para>
<para>
Konkrete Funktionen für einzelne Listeneinträge werden ausgeführt,
indem die Vorlagen im Modus
<link linkend="list.modus.list.apply"><tag class="attvalue">list.apply</tag></link>
auf einen Parameter ("Funktional") angewandt werden.
Typischerweise ist der Parameter eine Vorlage,
die auf sich selbst im Modus <tag class="attvalue">list.apply</tag> passt.
Zu jedem "Funktional"-Parameter gibt es einen weiteren Parameter
(meist mit dem Namen "<parameter>param</parameter>",
der an die Vorlagen im Modus <tag class="attvalue">list.apply</tag> übergeben wird.
</para>
</d:section>

<d:para>
Wurzel-Element dieser Datei.
Indem eine einbindende Datei dieses Variable anders definiert,
kann sie das Vorgabe-Verhalten fast aller Vorlagen dieser Datei ändern.
</d:para>
<xsl:variable name="list.docroot" select="document('')/xsl:stylesheet"/>

<d:section>
<d:para>
Listeneinträge akkumulieren.
Dem "Akkumulator" (Funktional-Parameter) werden die folgenden Parameter übergeben:
</d:para>
<d:variablelist>
  <d:varlistentry>
    <d:term><d:parameter>item</d:parameter></d:term>
    <d:para>der Listeneintrag</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>cur</d:parameter></d:term>
    <d:para>der bisher akkumulierte Wert</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>pos</d:parameter></d:term>
    <d:para>die Position des Listeneintrags</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>param</d:parameter></d:term>
    <d:para>der weitergegebene Parameter</d:para>
  </d:varlistentry>
</d:variablelist>
<d:para>
Der voreingestellte Akkumulator zählt die Einträge der Liste.
</d:para>
</d:section>
<xsl:template name="list.accumulate">
  <!-- die Liste -->
  <xsl:param name="list" select="."/>
  <!-- die Trennzeichenfolge -->
  <xsl:param name="sep" select="' '"/>
  <!-- der Akkumulator -->
  <xsl:param
    name = "accumulator"
    select = "$list.docroot/xsl:template [@name = 'list.accumulator.count']"
  />
  <!-- ein Parameter für den Akkumulator -->
  <xsl:param name="param"/>
  <!-- der aktuelle Wert, sollte in der Regel nicht angegeben werden -->
  <xsl:param name="cur"/>
  <!-- die aktuelle Position, nur für Ausnahmefälle -->
  <xsl:param name="pos" select="1"/>
  <xsl:choose>
    <xsl:when test="string-length ($list) = 0">
      <xsl:value-of select="$cur"/>
    </xsl:when>
    <xsl:when test="string-length ($sep) = 0 or not (contains ($list, $sep))">
      <xsl:apply-templates select="$accumulator" mode="list.apply">
        <xsl:with-param name="item" select="$list"/>
        <xsl:with-param name="cur" select="$cur"/>
        <xsl:with-param name="pos" select="$pos"/>
        <xsl:with-param name="param" select="$param"/>
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="list.accumulate">
        <xsl:with-param name="list" select="substring-after ($list, $sep)"/>
        <xsl:with-param name="sep" select="$sep"/>
        <xsl:with-param name="accumulator" select="$accumulator"/>
        <xsl:with-param name="cur">
          <xsl:apply-templates select="$accumulator" mode="list.apply">
            <xsl:with-param name="item" select="substring-before ($list, $sep)"/>
            <xsl:with-param name="cur" select="$cur"/>
            <xsl:with-param name="pos" select="$pos"/>
            <xsl:with-param name="param" select="$param"/>
          </xsl:apply-templates>
        </xsl:with-param>
        <xsl:with-param name="pos" select="$pos + 1"/>
        <xsl:with-param name="param" select="$param"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template><!-- list.accumulate -->

<d:para>
Dieser "Akkumulator" zählt die Einträge der Liste.
Er ist in der benannten Vorlage
<d:tag class="attvalue">list.accumulate</d:tag> voreingestellt.
</d:para>
<xsl:template
  name = "list.accumulator.count"
  mode = "list.apply"
  match =
  "xsl:template [@name = 'list.accumulator.count' and @mode = 'list.apply' and @match]"
>
  <xsl:param name="pos"/>
  <xsl:value-of select="$pos"/>
</xsl:template>

<d:section>
<d:para>
Fügt zwei Listen elementweise ("reißverschlussartig") zu einer neuen Liste zusammen.
Dem "Zipper" (Funktional-Parameter) werden die folgenden Parameter übergeben:
</d:para>
<d:variablelist>
  <d:varlistentry>
    <d:term><d:parameter>first</d:parameter></d:term>
    <d:para>der Eintrag der ersten Liste</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>second</d:parameter></d:term>
    <d:para>der Eintrag der zweiten Liste</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>param</d:parameter></d:term>
    <d:para>der weitergegebene Parameter</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>sep</d:parameter></d:term>
    <d:para>die Trennzeichenfolge der Ergebnis-Liste</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>pos</d:parameter></d:term>
    <d:para>
      die Position der Einträge in den Listen
      (<d:parameter>first</d:parameter> und
      <d:parameter>second</d:parameter> haben dieselbe Position)
    </d:para>
  </d:varlistentry>
</d:variablelist>
<d:para>
Das Ergebnis des "Zippers" ist ein einzelner Eintrag oder eine Liste von Einträgen,
die in die Ergebnisliste eingefügt werden.
Wenn das Ergebnis leer ist, wird kein Eintrag in die Ergebnisliste eingefügt.
</d:para>
<d:para>
Der voreingestellte "Zipper" fügt <d:parameter>first</d:parameter>,
<d:parameter>param</d:parameter>
und <d:parameter>second</d:parameter> zu einer Zeichenkette zusammen.
</d:para>
</d:section>
<xsl:template name="list.zip">
  <!-- die erste Liste -->
  <xsl:param name="first"/>
  <!-- die zweite Liste -->
  <xsl:param name="second"/>
  <!--
    dieser "Funktional"-Parameter bestimmt die Vorlage
    zur Verknüpfung zweier Listeneinträge
  -->
  <xsl:param name="zipper" select="$list.docroot/xsl:template [@name = 'list.zip.zipper']"
  />
  <!-- ein Parameter für den "zipper" -->
  <xsl:param name="param"/>
  <!-- der Trenner der Ergebnisliste -->
  <xsl:param name="sep" select="' '"/>
  <!-- der Trenner der ersten Liste -->
  <xsl:param name="sep_1" select="$sep"/>
  <!-- der Trenner der zweiten Liste -->
  <xsl:param name="sep_2" select="$sep"/>
  <!--
    wird am Anfang ein Listentrenner benötigt? 0 nein / 1 ja,
    sollte in der Regel nicht benutzt werden.
  -->
  <xsl:param name="need_sep" select="0"/>
  <!-- Position in den beiden Listen (beginnend bei 1), für Ausnahmefälle -->
  <xsl:param name="pos" select="1"/>
  <xsl:if test="string-length ($first) &gt; 0 or string-length ($second) &gt; 0">
    <xsl:variable name="item">
      <xsl:apply-templates select="$zipper" mode="list.apply">
        <xsl:with-param name="first">
          <xsl:call-template name="txt.firstentry">
            <xsl:with-param name="txt" select="$first"/>
            <xsl:with-param name="sep" select="$sep_1"/>
          </xsl:call-template>
        </xsl:with-param>
        <xsl:with-param name="second">
          <xsl:call-template name="txt.firstentry">
            <xsl:with-param name="txt" select="$second"/>
            <xsl:with-param name="sep" select="$sep_2"/>
          </xsl:call-template>
        </xsl:with-param>
        <xsl:with-param name="param" select="$param"/>
        <xsl:with-param name="sep" select="$sep"/>
        <xsl:with-param name="pos" select="$pos"/>
      </xsl:apply-templates>
    </xsl:variable>
    <xsl:if test="$need_sep &gt; 0 and string-length ($item) &gt; 0">
      <xsl:value-of select="$sep"/>
    </xsl:if>
    <xsl:value-of select="$item"/>
    <xsl:call-template name="list.zip">
      <xsl:with-param name="first" select="substring-after ($first,  $sep_1)"/>
      <xsl:with-param name="second" select="substring-after ($second, $sep_2)"/>
      <xsl:with-param name="zipper" select="$zipper"/>
      <xsl:with-param name="param" select="$param"/>
      <xsl:with-param name="sep" select="$sep"/>
      <xsl:with-param name="sep_1" select="$sep_1"/>
      <xsl:with-param name="sep_2" select="$sep_2"/>
      <xsl:with-param name="need_sep">
        <xsl:choose>
          <xsl:when test="$need_sep &gt; 0 or string-length ($item) &gt; 0">1</xsl:when>
          <xsl:otherwise>0</xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="pos" select="$pos + 1"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template><!-- list.zip -->

<d:para>
Dieser "Zipper" fügt <d:parameter>first</d:parameter>, <d:parameter>param</d:parameter>
und <d:parameter>second</d:parameter> zu einer Zeichenkette zusammen.
Er ist in der Vorlage <d:tag class="attvalue">list.zip</d:tag> voreingestellt.
</d:para>
<xsl:template
  name = "list.zip.zipper"
  mode = "list.apply"
  match = "xsl:template [@name = 'list.zip.zipper' and @mode = 'list.apply' and @match]"
>
  <!-- der erste Listeneintrag -->
  <xsl:param name="first"/>
  <!-- der zweite Listeneintrag -->
  <xsl:param name="second"/>
  <!-- der Trenner der Ergebnis-Liste -->
  <xsl:param name="sep" select="' '"/>
  <!-- die Trennzeichenfolge zwischen den zusammengefügten Listeneinträgen -->
  <xsl:param name="param" select="''"/>
  <xsl:if test="string-length ($first) &gt; 0 and string-length ($second) &gt; 0">
    <xsl:value-of select="concat ($first, $param, $second)"/>
  </xsl:if>
</xsl:template><!-- list.zip.zipper -->

<d:section>
<d:para>
Wendet den "Mapper" (Funktional-Parameter) auf jeden Listeneintrag an
und fügt die Ergebnisse des Mappers zur Ergebnisliste zusammen.
Das Ergebnis des "Mappers" kann leer, ein einzelner Listeneintrag
oder eine Liste von Einträgen sein.
Wenn das Ergebnis des "Mappers" leer ist,
wird kein Eintrag in die Ergebnisliste eingefügt.
Diese Vorlage kann insbesondere dazu benutzt werden,
bestimmte Einträge aus der Liste auszuwählen.
</d:para>
<d:para>
Die Parameter des "Mappers" sind
</d:para>
<d:variablelist>
  <d:varlistentry>
    <d:term><d:parameter>item</d:parameter></d:term>
    <d:listitem>
      <d:para>der Listeneintrag</d:para>
    </d:listitem>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>param</d:parameter></d:term>
    <d:listitem>
      <d:para>der weitergegebene Parameter</d:para>
    </d:listitem>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>sep</d:parameter></d:term>
    <d:listitem>
      <d:para>die Trennzeichenfolge der Ergebnis-Liste</d:para>
    </d:listitem>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>pos</d:parameter></d:term>
    <d:listitem>
      <d:para>die Position des Listeneintrags</d:para>
    </d:listitem>
  </d:varlistentry>
</d:variablelist>
</d:section>
<xsl:template name="list.map">
  <!-- die Liste -->
  <xsl:param name="list"/>
  <!--
    Funktional-Parameter:
    die Vorlage für dieses Element wird für jeden Listeneintrag angewendet
  -->
  <xsl:param name="mapper"/>
  <!-- zusätzlicher Parameter für die "mapper"-Vorlage -->
  <xsl:param name="param"/>
  <!-- die Trennzeichenfolge der (Eingangs-)Liste -->
  <xsl:param name="sep" select="' '"/>
  <!-- die Trennzeichenfolge der Ausgabeliste -->
  <xsl:param name="sepres" select="$sep"/>
  <!-- ist ein Trenner in der Ausgabe nötig? (nur für Ausnahmefälle) -->
  <xsl:param name="need_sep" select="0"/>
  <!-- aktuelle Position in der Eingabeliste, beginnend mit 1 (nur für Ausnahmefälle) -->
  <xsl:param name="pos" select="1"/>

  <xsl:choose>
    <xsl:when test="string-length ($list) = 0"/>
    <xsl:otherwise>
      <xsl:variable name="newitem">
        <xsl:apply-templates select="$mapper" mode="list.apply">
          <xsl:with-param name="item">
            <xsl:call-template name="txt.firstentry">
              <xsl:with-param name="txt" select="$list"/>
              <xsl:with-param name="sep" select="$sep"/>
            </xsl:call-template>
          </xsl:with-param>
          <xsl:with-param name="param" select="$param"/>
          <xsl:with-param name="sep" select="$sepres"/>
          <xsl:with-param name="pos" select="$pos"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:if test="string-length ($newitem) &gt; 0">
        <xsl:if test="$need_sep &gt; 0">
          <xsl:value-of select="$sepres"/>
        </xsl:if>
        <xsl:value-of select="$newitem"/>
      </xsl:if>
      <xsl:if test="string-length ($sep) &gt; 0 and contains ($list, $sep)">
        <xsl:call-template name="list.map">
          <xsl:with-param name="list" select="substring-after ($list, $sep)"/>
          <xsl:with-param name="mapper" select="$mapper"/>
          <xsl:with-param name="param" select="$param"/>
          <xsl:with-param name="sep" select="$sep"/>
          <xsl:with-param name="sepres" select="$sepres"/>
          <xsl:with-param name="need_sep">
            <xsl:choose>
              <xsl:when test="$need_sep &gt; 0 or string-length ($newitem) &gt; 0">
                <xsl:text>1</xsl:text>
              </xsl:when>
              <xsl:otherwise>0</xsl:otherwise>
            </xsl:choose>
          </xsl:with-param>
          <xsl:with-param name="pos" select="$pos + 1"/>
        </xsl:call-template>
      </xsl:if>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template><!-- list.map -->

<d:para>
Dieser "Mapper" (Parameter für <tag class="attvalue">list.map</tag>),
wählt Einträge an ungerader Position aus.
Er wird benutzt, um eine zu sortierende Liste zu zerlegen.
</d:para>
<xsl:template
  name = "list.map.select_odd"
  mode = "list.apply"
  match =
  "xsl:template [@name = 'list.map.select_odd' and @mode = 'list.apply' and @match]"
>
  <!-- der Listeneintrag -->
  <xsl:param name="item"/>
  <!-- die Position des Listeneintrags -->
  <xsl:param name="pos"/>
  <xsl:if test="$pos mod 2 != 0">
    <xsl:value-of select="$item"/>
  </xsl:if>
</xsl:template><!-- list.map.select_odd -->

<d:para>
Ein "Mapper" (Parameter für <tag class="attvalue">list.map</tag>),
wählt alle Einträge aus.
Dieser Mapper ist sinnvoll, wenn die Ergebnisliste nur eine andere Trennzeichenfolge
verwendet als die Eingabeliste.
Die Hilfsvorlage <d:tag class="attvalue">list.mergesorted</d:tag> bentzt ihn.
</d:para>
<xsl:template
  name = "list.map.all"
  mode = "list.apply"
  match = "xsl:template [@name = 'list.map.all' and @mode = 'list.apply' and @match]"
>
  <!-- der Listeneintrag -->
  <xsl:param name="item"/>
  <xsl:value-of select="$item"/>
</xsl:template><!-- list.map.all -->

<d:section>
<d:para>
Wendet den "Mapper" (Funktional-Parameter) auf die Listeneinträge an.
Das Ergebnis ist das erste nicht-leere Ergebnis des "Mappers".
</d:para>
<d:para>
Die Parameter des "Mappers" sind
</d:para>
<d:variablelist>
  <d:varlistentry>
    <d:term><d:parameter>item</d:parameter></d:term>
    <d:listitem>
      <d:para>der Listeneintrag</d:para>
    </d:listitem>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>param</d:parameter></d:term>
    <d:listitem>
      <d:para>der weitergegebene Parameter</d:para>
    </d:listitem>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>pos</d:parameter></d:term>
    <d:listitem>
      <d:para>die Position des Listeneintrags</d:para>
    </d:listitem>
  </d:varlistentry>
</d:variablelist>
</d:section>
<xsl:template name="list.map_once">
  <!-- die Liste -->
  <xsl:param name="list"/>
  <!--
    Funktional-Parameter:
    die Vorlage für dieses Element wird für jeden Listeneintrag angewendet
  -->
  <xsl:param name="mapper"/>
  <!-- zusätzlicher Parameter für die "mapper"-Vorlage -->
  <xsl:param name="param"/>
  <!-- die Trennzeichenfolge der (Eingangs-)Liste -->
  <xsl:param name="sep" select="' '"/>
  <!-- aktuelle Position in der Eingabeliste, beginnend mit 1 (nur für Ausnahmefälle) -->
  <xsl:param name="pos" select="1"/>

  <xsl:choose>
    <xsl:when test="string-length ($list) = 0"/>
    <xsl:otherwise>
      <xsl:variable name="newitem">
        <xsl:apply-templates select="$mapper" mode="list.apply">
          <xsl:with-param name="item">
            <xsl:call-template name="txt.firstentry">
              <xsl:with-param name="txt" select="$list"/>
              <xsl:with-param name="sep" select="$sep"/>
            </xsl:call-template>
          </xsl:with-param>
          <xsl:with-param name="param" select="$param"/>
          <xsl:with-param name="pos" select="$pos"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="string-length ($newitem) &gt; 0">
          <xsl:value-of select="$newitem"/>
        </xsl:when>
        <xsl:when test="string-length ($sep) &gt; 0 and contains ($list, $sep)">
          <xsl:call-template name="list.map_once">
            <xsl:with-param name="list" select="substring-after ($list, $sep)"/>
            <xsl:with-param name="mapper" select="$mapper"/>
            <xsl:with-param name="param" select="$param"/>
            <xsl:with-param name="sep" select="$sep"/>
            <xsl:with-param name="pos" select="$pos + 1"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise/>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<d:para>
Fügt zwei sortierte Listen sortiert zusammen.
Diese Hilfsvorlage wird von <d:tag class="attvalue">list.sort</d:tag> benutzt
und sollte in der Regel nicht direkt aufgerufen werden.
</d:para>
<xsl:template name="list.mergesorted">
  <!-- die erste sortierte Liste -->
  <xsl:param name="list_1"/>
  <!-- die zweite sortierte Liste -->
  <xsl:param name="list_2"/>
  <!--
      die Trennzeichenfolge der Ergebnis-Liste,
      zugleich Voreinstellung der Trennzeichenfolgen der Eingangs-Listen
  -->
  <xsl:param name="sep" select="' '"/>
  <!-- die Trennzeichenfolge der ersten Liste -->
  <xsl:param name="sep_1" select="$sep"/>
  <!-- die Trennzeichenfolge der zweiten Liste -->
  <xsl:param name="sep_2" select="$sep"/>
  <!--
    Funktional-Parameter (Vergleicher):
    bestimmt die Vorlage zum Vergleich zweier Elemente
  -->
  <xsl:param name="sorter" select="$list.docroot/xsl:template [@name = 'list.compare.integer']"
  />
  <!-- ein Parameter für der Sortierer -->
  <xsl:param name="param"/>
  <!-- ist vor dem nächsten Eintrag im Ergebnis ein Trenner nötig? -->
  <xsl:param name="need_sep" select="0"/>
  <xsl:choose>
    <xsl:when test="string-length ($list_1) = 0 or string-length ($list_2) = 0">
      <xsl:call-template name="list.map">
        <xsl:with-param name="list">
          <xsl:choose>
            <xsl:when test="string-length ($list_1) = 0">
              <xsl:value-of select="$list_2"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$list_1"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="mapper" select="$list.docroot/xsl:template [@name = 'list.map.all']"
        />
        <xsl:with-param name="sep">
          <xsl:choose>
            <xsl:when test="string-length ($list_1) = 0">
              <xsl:value-of select="$sep_2"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$sep_1"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="sepres" select="$sep"/>
        <xsl:with-param name="need_sep" select="$need_sep"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="i1">
        <xsl:call-template name="txt.firstentry">
          <xsl:with-param name="txt" select="$list_1"/>
          <xsl:with-param name="sep" select="$sep_1"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="i2">
        <xsl:call-template name="txt.firstentry">
          <xsl:with-param name="txt" select="$list_2"/>
          <xsl:with-param name="sep" select="$sep_2"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="comp">
        <xsl:apply-templates select="$sorter" mode="list.apply">
          <xsl:with-param name="a" select="$i1"/>
          <xsl:with-param name="b" select="$i2"/>
          <xsl:with-param name="param" select="$param"/>
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:if test="$need_sep != 0"><xsl:value-of select="$sep"/></xsl:if>
      <xsl:choose>
        <xsl:when test="$comp &gt;= 0">
          <xsl:value-of select="$i1"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$i2"/>
        </xsl:otherwise>
      </xsl:choose>
      <xsl:call-template name="list.mergesorted">
        <xsl:with-param name="list_1">
          <xsl:choose>
            <xsl:when test="$comp &gt;= 0">
              <xsl:value-of select="substring-after ($list_1, $sep_1)"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$list_1"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="list_2">
          <xsl:choose>
            <xsl:when test="$comp &gt;= 0">
              <xsl:value-of select="$list_2"/>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="substring-after ($list_2, $sep_2)"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
        <xsl:with-param name="sep" select="$sep"/>
        <xsl:with-param name="sep_1" select="$sep_1"/>
        <xsl:with-param name="sep_2" select="$sep_2"/>
        <xsl:with-param name="sorter" select="$sorter"/>
        <xsl:with-param name="param" select="$param"/>
        <xsl:with-param name="need_sep" select="1"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template><!-- list.mergesorted -->

<d:section>
<d:para>
Sortiert eine Liste.
Die Parameter des "Vergleichers" sind nach der Perl-Konvention benannt:
</d:para>
<d:variablelist>
  <d:varlistentry>
    <d:term><d:parameter>a</d:parameter></d:term>
    <d:para>der erste der beiden zu vergleichenden Werte</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>b</d:parameter></d:term>
    <d:para>der zweite der zu vergleichenden Werte</d:para>
  </d:varlistentry>
  <d:varlistentry>
    <d:term><d:parameter>param</d:parameter></d:term>
    <d:para>der weitergegebene Parameter für den Vergleicher</d:para>
  </d:varlistentry>
</d:variablelist>
<d:para>
Ein negatives Ergebnis bedeutet,
dass <d:parameter>a</d:parameter> nach <d:parameter>b</d:parameter> folgt;
ein positives Ergebnis bedeutet,
dass <d:parameter>b</d:parameter> nach <d:parameter>a</d:parameter> folgt;
Null bedeutet,
dass <d:parameter>a</d:parameter> und <d:parameter>a</d:parameter> gleichrangig sind.
</d:para>
<d:para>
Der voreingestellte Vergleicher <d:tag class="attvalue">list.compare.integer</d:tag>
interpretiert führende Dezimalziffern eines Listeneintrags als Zahl
und ordnet Listeneinträge nach dieser Zahl.
</d:para>
</d:section>
<xsl:template name="list.sort">
  <!-- die zu sortierende Liste -->
  <xsl:param name="list"/>
  <!-- die Trennzeichenfolge der Liste -->
  <xsl:param name="sep" select="' '"/>
  <!-- Funktional-Parameter (Vergleicher): bestimmt die Vergleich-Vorlage -->
  <xsl:param name="sorter" select="$list.docroot/xsl:template [@name = 'list.compare.integer']"
  />
  <!-- ein Parameter für die Vergleich-Vorlage -->
  <xsl:param name="param"/>
  <!-- die Trennzeichenfolge der sortierten Liste -->
  <xsl:param name="sepres" select="$sep"/>
  <xsl:choose>
    <xsl:when test="string-length ($sep) = 0 or not (contains ($list, $sep))">
      <xsl:value-of select="$list"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="list.mergesorted">
        <xsl:with-param name="list_1">
          <xsl:call-template name="list.sort">
            <xsl:with-param name="list">
              <xsl:call-template name="list.map">
                <xsl:with-param name="list" select="$list"/>
                <xsl:with-param name="mapper" select="$list.docroot/xsl:template [@name = 'list.map.select_odd']"
                />
                <xsl:with-param name="sep" select="$sep"/>
                <xsl:with-param name="sepres" select="$sepres"/>
              </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="sep" select="$sepres"/>
            <xsl:with-param name="sorter" select="$sorter"/>
            <xsl:with-param name="param" select="$param"/>
          </xsl:call-template>
        </xsl:with-param>
        <xsl:with-param name="list_2">
          <xsl:call-template name="list.sort">
            <xsl:with-param name="list">
              <xsl:call-template name="list.map">
                <xsl:with-param name="list" select="substring-after ($list, $sep)"/>
                <xsl:with-param name="mapper" select="$list.docroot/xsl:template [@name = 'list.map.select_odd']"
                />
                <xsl:with-param name="sep" select="$sep"/>
                <xsl:with-param name="sepres" select="$sepres"/>
              </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="sep" select="$sepres"/>
            <xsl:with-param name="sorter" select="$sorter"/>
            <xsl:with-param name="param" select="$param"/>
          </xsl:call-template>
        </xsl:with-param>
        <xsl:with-param name="sep" select="$sepres"/>
        <xsl:with-param name="sorter" select="$sorter"/>
        <xsl:with-param name="param" select="$param"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template><!-- list.sort -->

<d:para>
Vergleicht die führenden Ziffernfolgen zweier Elemente als natürliche Zahlen.
Dieser Vergleicher ist in der Vorlage <d:tag class="attvalue">list.sort</d:tag>
voreingestellt.
</d:para>
<xsl:template
  name = "list.compare.integer"
  mode = "list.apply"
  match =
  "xsl:template [@name = 'list.compare.integer' and @mode = 'list.apply' and @match]"
>
  <xsl:param name="a"/>
  <xsl:param name="b"/>
  <xsl:variable name="digits" select="'0123456789'"/>
  <xsl:variable name="na">
    <xsl:call-template name="txt.firstcharsin">
      <xsl:with-param name="txt" select="$a"/>
      <xsl:with-param name="chars" select="$digits"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="nb">
    <xsl:call-template name="txt.firstcharsin">
      <xsl:with-param name="txt" select="$b"/>
      <xsl:with-param name="chars" select="$digits"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="ia">
    <xsl:choose>
      <xsl:when test="string-length ($na) = 0">0</xsl:when>
      <xsl:otherwise><xsl:value-of select="number ($na)"/></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="ib">
    <xsl:choose>
      <xsl:when test="string-length ($na) = 0">0</xsl:when>
      <xsl:otherwise><xsl:value-of select="number ($nb)"/></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:value-of select="$ib - $ia"/>
</xsl:template><!-- list.compare.integer -->

<d:para>
Diese Vorlage kombiniert den Aufruf der benannten Vorlagen
<d:tag class="attvalue">list.map</d:tag>
und <d:tag class="attvalue">list.sort</d:tag>:
zunächst <d:tag class="attvalue">list.map</d:tag>,
dann <d:tag class="attvalue">list.sort</d:tag>.
</d:para>
<xsl:template name="list.map_sort">
  <!-- die Liste -->
  <xsl:param name="list"/>
  <!-- Funktional-Parameter: der "Mapper" -->
  <xsl:param name="mapper" select="$list.docroot/xsl:template [@name = 'list.map.all']"/>
  <!-- ein Parameter für den "Mapper" -->
  <xsl:param name="map_param"/>
  <!-- Funktional-Parameter: der Vergleicher -->
  <xsl:param name="sorter" select="$list.docroot/xsl:template [@name = 'list.compare.integer']"
  />
  <!-- ein Parameter für den Vergleicher -->
  <xsl:param name="sort_param"/>
  <!-- die Trennzeichenfolge der (Eingangs-)Liste -->
  <xsl:param name="sep" select="' '"/>
  <!-- die Trennzeichenfolge der Ausgabeliste -->
  <xsl:param name="sepres" select="$sep"/>
  <xsl:call-template name="list.sort">
    <xsl:with-param name="list">
      <xsl:call-template name="list.map">
        <xsl:with-param name="list" select="$list"/>
        <xsl:with-param name="mapper" select="$mapper"/>
        <xsl:with-param name="param" select="$map_param"/>
        <xsl:with-param name="sep" select="$sep"/>
        <xsl:with-param name="sepres" select="$sepres"/>
      </xsl:call-template>
    </xsl:with-param>
    <xsl:with-param name="sep" select="$sepres"/>
    <xsl:with-param name="sorter" select="$sorter"/>
    <xsl:with-param name="param" select="$sort_param"/>
  </xsl:call-template>
</xsl:template><!-- list.map_sort -->

<d:para>
Wählt einen Listeneintrag nach einem enthaltenen Schlüssel aus.
Das Ergebnis ist der erste Listeneintrag,
der die Zeichenkette <d:parameter>key</d:parameter> enthält, oder leer.
Das Ergebnis ist auch leer, wenn <d:parameter>key</d:parameter> leer ist.
</d:para>
<xsl:template name="list.entry_by_key">
  <!-- die Liste -->
  <xsl:param name="list"/>
  <!-- der zu suchende Schlüssel -->
  <xsl:param name="key"/>
  <!-- die Trennzeichenfolge der Liste -->
  <xsl:param name="sep" select="' '"/>
  <xsl:choose>
    <xsl:when test="string-length ($key) = 0 or not (contains ($list, $key))"/>
    <xsl:otherwise>
      <xsl:call-template name="txt.substring_afterlast">
        <xsl:with-param name="txt" select="substring-before ($list, $key)"/>
        <xsl:with-param name="what" select="$sep"/>
      </xsl:call-template>
      <xsl:value-of select="substring-before (concat ($key, substring-after ($list, $key), $sep), $sep)"
      />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template><!-- list.entry_by_key -->

<!--
<d:para>Test</d:para>
<xsl:template name = "test.list.map">
  <xsl:variable name = "testlist"
    select = "concat (
      '13auto 9bahn 27bau 12stellen 1verkehr 7führungs',
      ' 7ampel 29anlagen 37steuerungs 111software 333fehl 0funktion'
    )"
  />
  <xsl:variable name = "list_1">
    <xsl:call-template name = "list.map">
      <xsl:with-param name = "list" select = "$testlist"/>
      <xsl:with-param name = "mapper"
        select = "$list.docroot/xsl:template [@name = 'list.map.select_odd']"
      />
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name = "list_2">
    <xsl:call-template name = "list.map">
      <xsl:with-param name = "list" select = "substring-after ($testlist, ' ')"/>
      <xsl:with-param name = "mapper"
        select = "$list.docroot/xsl:template [@name = 'list.map.select_odd']"
      />
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name = "merged">
    <xsl:call-template name = "list.mergesorted">
      <xsl:with-param name = "list_1" select = "$list_1"/>
      <xsl:with-param name = "list_2" select = "$list_2"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name = "sorted_1">
    <xsl:call-template name = "list.sort">
      <xsl:with-param name = "list" select = "$list_1"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name = "sorted">
    <xsl:call-template name = "list.sort">
      <xsl:with-param name = "list" select = "$testlist"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select = "concat ($txt.break, 'testlist : ', $testlist)"/>
  <xsl:value-of select = "concat ($txt.break, 'sorted   : ', $sorted)"/>
  <xsl:value-of select = "concat ($txt.break, 'list_1   : ', $list_1)"/>
  <xsl:value-of select = "concat ($txt.break, 'list_2   : ', $list_2)"/>
  <xsl:value-of select = "concat ($txt.break, 'merged   : ', $merged)"/>
  <xsl:value-of select = "concat ($txt.break, 'sorted_1 : ', $sorted_1)"/>
  <xsl:value-of select = "$txt.break"/>
</xsl:template>
-->

</xsl:stylesheet>
