XPath- und Layoutfunktionen (neuer XPath Parser)
Diese Seite beschreibt den neuen XPath-Parser, genannt »lxpath«. Es gibt das alte XPath-Modul namens »luxor«. Um zwischen diesen beiden Modulen zu wechseln, können Sie die Standardeinstellung in der XPath-Konfiguration festlegen, zum Beispiel auf der Kommandozeile mit
sp --xpath luxor
(das alte Modul)
oder
sp --xpath lxpath
für den neuen XPath-Parser (Voreinstellung). Sie können diese Einstellung auch in der Konfigurationsdatei vornehmen.
Was ist XPath und warum gibt es zwei verschiedene Implementierungen?
Die Eingabe des speedata Publishers sind im XML-Format kodierte Daten. XML ist eine hierarchische Datenstruktur, in der es ein Wurzelelement gibt und jedes Element Attribute und Kinder haben kann, die entweder Text oder andere Elemente sind. Sie können beispielsweise ein Element Artikelgruppe haben, das zehn Artikel-Elemente enthält. Die Idee von XPath ist es nun, durch den XML-Baum zu navigieren und Fragen zu stellen wie: Gib mir alle Artikel, die ein bestimmtes Attribut haben. Oder wie viele Artikel habe ich in dieser Artikelgruppe?
Bisher verwendet der speedata-Publisher eine Ad-hoc-Implementierung eines XML-Parsers, der sich zwar bewährt hat, aber nicht sehr robust ist und nicht viele Fehler mit falschen Daten meldet. Auch der XPath-Parser hat hauptsächlich mit regulären Ausdrücken und globalen Zuständen gearbeitet, was ebenfalls nicht sehr robust ist.
Die neue Implementierung verwendet den XML-Parser aus der Standardbibliothek von Go und eine XPath-Implementierung, die neu ist und mit der XPath Grammatik im Hinterkopf entwickelt wurde.
Vorteile der neuen XML/XPath-Implementierung
- Die gesamte Implementierung ist schneller.
- Es gibt bessere Fehlermeldungen (zum Beispiel sind Zeilennummern Teil der Fehlermeldungen).
- Sie können Ihre eigenen Funktionsdefinitionen haben.
- Der neue XPath-Parser wird im speedata Publisher ab Version 4.18 standardmäßig verwendet.
XPath Ausdrücke
Der Publisher akzeptiert in den den entsprechend markierten Attributen (zumeist select
und test
) XPath Ausdrücke.
In allen anderen Attributen kann durch die geschweiften Klammern ({
und }
) ein XPath Ausdruck erzwungen werden.
Siehe z.B. das Layout im examples-Repository für Optimierung mit Gruppen.
In diesem Beispiel werden im Attribut width
und im Element Value
die Werte dynamisch erzeugt, d. h. für die
Angabe der Breite wird auf den Inhalt der Variablen breite
zurückgegriffen, der Inhalt des Absatzes ist der Inhalt (Textwert) des gerade aktuellen Datenknotens.
<Textblock width="{$breite}">
<Paragraph>
<Value select="."/>
</Paragraph>
</Textblock>
Folgende XPath-Ausdrücke erkennt das System:
-
Zahl: gibt den Wert direkt zurück. Beispiel:
"5"
-
Text: gibt den Wert direkt zurück. Beispiel:
'Text'
-
Rechenoperationen (
*
,div
,idiv
,+
,-
,mod
). Beispiel:( 6 + 4.5 ) * 2
-
Zugriff auf Variablen. Beispiel:
$spalte + 2
-
Zugriff auf den aktuellen Knoten (Punkt-Operator). Beispiel:
. + 2
-
Zugriff auf Unterelemente. Beispiel:
produktdaten
,*
,foo/bar
,node()
. -
Zugriff auf Elternelement:
../
. -
Filter in eckigen Klammern:
article[1]
wählt das erste Kindelementarticle
aus. -
Zugriff auf Attribute im aktuellen Knoten. Beispiel
@a
. -
Zugriff auf Unterelemente. Beispiel:
produktdaten
,*
,foo/bar
-
Zugriff auf Attribute im aktuellen Knoten. Beispiel
@a
-
Zugriff auf Attribute in Kindelementen, zum Beispiel
foo/@bar
. -
Boolesche Ausdrücke:
<
,>
,<=
,>=
,=
,!=
. Vorsicht: das Zeichen<
muss in XML als<
geschrieben werden, das Zeichen>
kann als>
geschrieben werden. Beispiel:$zahl > 6
. Kann in Bedingungen benutzt werden. -
if
/then
/else
-Abfragen:if (…) then … else …
-
for-Ausdrücke, z.B.:
for $i in (1,2,3) return $i * 2
oderfor $i in 1 to 3 return $i * 2
. - Die bekannten Achsen wie preceding-sibling, parent oder descendant-or-self.
Immer noch im Unklaren, wie XPath funktioniert? Bei W3Schools gibt es ein gutes Tutorial.
Layout-Funktionen
Es gibt drei Klassen von Funktionen: standardkonforme (XPath-), speedata Publisher Layout-Funktionen und selbstdefinierte Funktionen.
Die Layout-Funktionen sind im Namensraum urn:speedata:2009/publisher/functions/de
(im Folgenden mit sd:
gekennzeichnet) definiert. Optionale Parameter werden durch eckige Klammern markiert.
Beispiel: die Funktion sd:current-row()
kann auch mit Parameter sd:current-row('name')
aufgerufen werden. Die Auslassungspunkte …
bedeuten, dass der letzte Wert beliebig oft wiederholt werden kann.
sd:allocated(x,y,<Bereichsname>,<Rahmennummer>)
-
Gibt wahr zurück, wenn die Zelle belegt ist (seit 2.3.71).
sd:alternating(<typ>, <text>[,<text>,…])
-
Bei jedem Aufruf wird das nächste Argument zurück gegeben. Wert des Typs ist beliebig, muss aber eindeutig sein. Beispiel:
sd:alternating("tbl", "Weiß","Grau")
könnte für die Hintergrundfarbe von Tabellen benutzt werden. Beispiel siehe Abwechselnde Zeilenfarben. sd:aspectratio(<Bildname>,[Seitenzahl],[pdfbox])
-
Gibt das Ergebnis der Division Bildbreite / Bildhöhe zurück. (D. h. < 1 für Hochkantbilder, > 1 für Querformat.) Die Parameter können die Seitenzahl und die PDF-Box enthalten, siehe Angabe der Seite bei Layout-Funktionen.
sd:attr(<Name>[, …])
-
ist dasselbe wie
@Name
, nur mit der Möglichkeit den Namen auch dynamisch (z. B. mitconcat()
) zu erzeugen. Siehe Beispiel beisd:variable()
. sd:count-saved-pages(<Name>)
-
Gibt die Anzahl der gespeicherten Seiten, die mit
<SavePages>
zwischengespeichert wurden. sd:current-column([<area>])
-
Gibt die aktuelle Spalte zurück. Wenn
area
angegeben, gibt die Spalte des gegebenen Platzierungsbereichs zurück. sd:current-framenumber(<area>)
-
Gibt die Nummer des aktuellen Rahmens im Platzierungsbereich zurück.
sd:current-page()
-
Gibt die Seitennummer zurück.
sd:current-row([<area>])
-
Gibt die aktuelle Zeile zurück. Wenn
area
angegeben, gibt die Zeile des gegebenen Platzierungsbereichs zurück. sd:decode-base64(<Zeichenkette>)
-
Konvertiert eine Base64-kodierte Zeichenkette und gibt den binären Inhalt zurück.
sd:decode-html(<Node>)
-
Wandelt Texte wie
<i>Kursiv</i>
in entsprechendes HTML-Markup. sd:dimexpr(<Einheit>,<Ausdruck>)
-
Interpretiert den Ausdruck als Rechenoperation und gibt den Wert als Skalar in der Einheit zurück. Interpretiert Variablen. Beispiel: Wenn
$twocm
auf die Zeichenkette2cm
gesetzt ist, ergibtsd:dimexpr('cm',' (40mm + $twocm) / 2 ')
die Zahl 3.0. sd:dummytext([<Anzahl>])
-
Gibt den Blindtext "Lorem ipsum…" mit über 50 Wörtern zurück. Mit dem optionalen Parameter kann man festlegen, wie oft der Text wiederholt wird.
sd:even(<zahl>)
-
Die Rückgabe ist
true()
, wenn die angegebene Zahl gerade ist. Beispiel:sd:even(sd:current-page())
sd:file-exists(<Dateiname oder URI-Schema>)
-
Die Rückgabe ist
true()
, wenn der Dateiname im Suchpfad existiert, ansonstenfalse()
. sd:filecontents(<binarycontent>)
-
Speichert den Inhalt in eine temporäre Datei und gibt den Namen zurück.
sd:firstmark(<pagenumber>)
-
Der erste Marker der angegebenen Seitenzahl. Hilfreich z.B. in Wörterbüchern, wo der erste und der letzte Begriff einer Seite ausgegeben werden.
sd:first-free-row(<name>)
-
Gib die erste freie Zeile des Bereichs zurück (experimentell).
sd:format-number(<Zahl oder String>, <Tausenderzeichen>, <Kommazeichen>)
-
Formatiert die übergebene Zahl und fügt Tausender-Trennzeichen hinzu und ändert den Kommatrenner. Beispiel:
sd:format-number(12345.67, '.',',')
ergibt die Zeichenkette12.345,67
. sd:format-string(<Objekt>,<Objekt>,…,<Formatierungsangaben>)
-
Gibt eine Zeichenkette zurück, die die gegebenen Objekte mit den im zweiten Argument gegebenen Formatierungsanweisungen darstellt. Die Formatierungsanweisungen entsprechen der aus der Programmiersprache C bekannten
printf()
-Funktion. sd:group-height(<string>[,<string>])
-
Gibt die Höhe in Rasterzellen für die Gruppe im ersten Argument an. Beispiel:
sd:group-height('Beispielgruppe')
. Ist ein zweites Argument angegeben, so wird die Gruppenhöhe als Vielfaches der Einheit genommen. Beispiel:sd:group-height('Beispielgruppe','mm')
gibt die genaue Höhe der Gruppe in mm an. sd:group-width(<string>[,<string>])
-
Gibt die Breite in Rasterzellen für die Gruppe im ersten Argument an. Beispiel:
sd:group-width('Beispielgruppe')
. Für das zweite Argument siehe die Beschreibung vonsd:group-height()
oben. sd:imageheight(<Dateiname oder URI-Schema>,[Seitenzahl],[pdfbox],[Einheit])
-
Höhe des Bildes in Rasterzellen. Vorsicht: sollte das Bild nicht gefunden werden, wird die Höhe des Platzhalters für nicht gefundene Bilder zurückgegeben. Daher muss vorher überprüft werden, ob das Bild existiert. Das letzte Argument ist eine Einheit. Wenn angegeben, ist die Bildbreite ein Vielfaches dieser Einheit. Die Parameter können die Seitenzahl und die PDF-Box enthalten, siehe Angabe der Seite bei Layout-Funktionen.
sd:imagewidth(<Dateiname oder URI-Schema>,[Seitenzahl],[pdfbox],[Einheit])
-
Breite des Bildes in Rasterzellen. Vorsicht: sollte das Bild nicht gefunden werden, wird die Breite des Platzhalters für nicht gefundene Bilder zurückgegeben. Daher muss vorher überprüft werden, ob das Bild existiert. Das zweite Argument ist eine Einheit. Wenn angegeben, ist die Bildbreite ein Vielfaches dieser Einheit. Die Parameter können die Seitenzahl und die PDF-Box enthalten, siehe Angabe der Seite bei Layout-Funktionen.
sd:keep-alternating(<typ>)
-
Benutzt den aktuellen Wert von
sd:alternating(<typ>)
, ohne diesen zu verändern. sd:lastmark(<pagenumber>)
-
Der letzte Marker der angegebenen Seitenzahl. Hilfreich z.B. in Wörterbüchern, wo der erste und der letzte Begriff einer Seite ausgegeben werden.
sd:length(was>[,<einheit>])
-
Gibt die Länge von was in Einheiten an (aber ohne die Einheit). Zum Beispiel
sd:length('_bleed','mm')
gibt den Wert3
zurick, falls der Anschnitt (bleed) auf 3mm gesetzt ist. Diese Funktion initialisiert eine Seite. Die Einheit ist auf 'mm' voreingestellt. was kann eines der Werte von_bleed
,_pagewidth
oder_pageheight
(Seit Version 4.19.10.) sd:loremipsum()
-
Alias für
sd:dummytext()
sd:markdown(<Text>)
-
Interpretiert den Text als Markdown. Siehe Markdown.
sd:md5(<Wert>[,<Wert>, …])
-
Erzeugt die MD5 Summe der Hintereinanderkettung der Werte als Hex-Zeichenkette. Beispiel:
sd:md5('Hallo ', 'Welt')
ergibt die Zeichenkette5c372a32c9ae748a4c040ebadc51a829
. sd:merge-pagenumbers(<Seitenzahlen>,[<Trenner für Bereiche>],[<Trenner für Leerraum>],[Hyperlinks])
-
Fasst Seitenzahlenbereiche zusammen. Beispielsweise aus
"1, 3, 4, 5"
wird1, 3–5
. Voreinstellung für den Trenner für Bereiche ist ein Halbgeviertstrich (–), Voreinstellung für den Trenner für Leerraum ist ', ' (Komma, Leerzeichen). Diese Funktion sortiert die Zahlen und löscht doppelte Einträge. Bei leerem Trenner für Bereiche werden Zahlen nicht zusammengeführt, sondern einzeln mit dem Trenner für Leerraum verbunden. Ist Hyperlinks auftrue()
gesetzt, werden die Seitenzahlen aktiv und führen über einen Klick zur jeweiligen Seite. Die Voreinstellung istfalse()
. Es werden die in der Anzeige die benutzerdefinierten Seitenzahlen verwendet, die in der Voreinstellung den echten Seitenzahlen entsprechen. sd:mode(<string>[,<string>…])
-
Gibt Wahr (
true()
) zurück, wenn einer der angegebenen Modi gesetzt ist. Ein Modus kann über die Kommandozeile oder über die Konfigurationsdatei gesetzt werden. Siehe Steuerung des Layouts beim Aufruf des Publishers. sd:number-of-columns([<area>])
-
Gibt die Anzahl der Spalten auf der Seite bzw. im angegebenen Bereich.
sd:number-of-pages(<Dateiname oder URI-Schema>)
-
Ermittelt die Anzahl der Seiten der angegebenen (PDF-)Datei. Siehe das Beispiel in Mehrseitige PDF-Dateien einbinden.
sd:number-of-rows([<area>])
-
Gibt die Anzahl der Zeilen auf der Seite bzw. im angegebenen Bereich.
sd:odd(<zahl>)
-
Die Rückgabe ist
true()
,, wenn die angegebene Zahl ungerade ist. sd:pagenumber(<Marke>)
-
Liefert die Seitenzahl der Seite auf der die angegebene Marke ausgegeben wurde. Siehe den Befehl Mark und den Abschnitt über Verzeichnisse erstellen.
sd:pageheight(<Einheit>)
-
Wie
sd:pagewidth()
, nur für die Höhe. sd:pagewidth(<Einheit>)
-
Erhalte die Breite der Seite in der angegebenen Einheit. Es wird eine Zahl ohne diese Einheit zurückgegeben. Beispiel für eine Seite mit 210mm Breite würde die Funktion
sd:pagewidth("mm")
die Zahl210
zurückgeben. Diese Funktion initialisiert eine Seite. (Seit Version 4.13.8.) sd:randomitem(<Wert>[,<Wert>,…])
-
Gibt einen der Werte zurück.
sd:reset-alternating(<typ>)
-
Setzt den Zustand für
sd:alternating()
für den angegebenen Typ zurück. sd:romannumeral(<Zahl>)
-
Konvertiere die Zahl in eine römische Zahl.
sd:sha1(<Wert>[,<Wert>, …])
-
Erzeugt die SHA-1 Summe der Hintereinanderkettung der Werte als Hex-Zeichenkette. Beispiel:
sd:sha1('Hallo ', 'Welt')
ergibt die Zeichenkette28cbbc72d6a52617a7abbfff6756d04bbad0106a
. sd:sha256(<Wert>[,<Wert>, …])
-
Erzeugt die SHA-256 Summe der Hintereinanderkettung der Werte als Hex-Zeichenkette. Beispiel:
sd:sha256('Hallo ', 'Welt')
ergibt die Zeichenkette2d2da19605a34e037dbe82173f98a992a530a5fdd53dad882f570d4ba204ef30
. sd:sha512(<Wert>[,<Wert>, …])
-
Erzeugt die SHA-512 Summe der Hintereinanderkettung der Werte als Hex-Zeichenkette. Beispiel:
sd:sha512('Hallo ', 'Welt')
ergibt die Zeichenkette6e32f66f62a8df494e45a2da0480189e108335301b76f03457caafcc996693c4c991683594fefc843739fe3a3f2a7d2593dff308d2549ecd0a791ef42d98a2cc
. sd:tounit(<Zeichenkette>,<Zeichenkette>[,<Zahl>])
-
Gibt einen skalaren Wert der Einheit im zweiten Argument konvertiert in die Einheit des ersten Arguments zurück. Das dritte Argument ist die Anzahl der Nachkommastellen auf die gerundet werden soll (Voreinstellung: 0 - runden auf Ganzzahlwerte). Beispiel:
sd:tounit('pt','1pc')
ergibt 12, da ein Pica (pc) 12 Punkt enthält. sd:variable-exists(<Name>)
-
Prüft, ob eine Variable definiert wurde.
sd:variable(<Name>[, …])
-
ist dasselbe wie
$Name
, nur mit der Möglichkeit den Namen auch dynamisch zu erzeugen. Falls$i
den Wert 3 enthält, liestsd:variable('foo',$i)
den Inhalt der Variablen$foo3
. Damit lassen sich Arrays abbilden. sd:visible-pagenumber(<Zahl>)
-
Liefert die Benutzerdefinerte Seitenzahl für die angegebene echte Seitenzahl zurück. Benutzerdefinierte Seitenzahlen können mit DefineMatter erzeugt werden.
XPath-Funktionen
abs(<Zahl>)
-
Liefert den positiven Wert der angegebenen Zahl zurück. Beispiel: sowohl
abs(-1.34)
als auchabs(-1.34)
ergeben die Zahl1.34
. boolean(<Sequenz>)
-
Gibt den effektiven Booleschen Wert der Sequenz zurück.
ceiling(<Zahl>)
-
Ergibt die nächst höhere Ganzzahl zurück.
ceiling(-1.34)
ergibt 1,ceiling(1.34)
ergibt 2. codepoints-to-string( <codepoints> )
-
Konvertiere die Sequenz von Codepoints in eine Zeichenkette.
concat(<Wert>,<Wert>, …)
-
Erzeugt einen neuen Text aus der Verkettung der einzelnen Werte.
contains(<heuhaufen>,<nadel>)
-
Wahr, wenn
heuhaufen
nadel
enthält. Beispiel:contains('bana','na')
ergibttrue()
. count()
-
Zählt alle Kindelemente mit dem angegebenen Namen. Beispiel:
count(eintrag)
zählt, wie viele Kindelemente mit den Nameneintrag
existieren. doc(<string>)
-
Öffnet die Datei mit dem angegebenen Dateinamen und gibt den Inhalt der Datei zurück.
empty(<Attribut>)
-
Ergibt wahr, wenn die Sequenz leer ist, z.B. wenn ein Attribut oder ein Element nicht vorhanden ist.
empty(@doesnotexist)
ergibttrue()
undempty(@nonempty)
ergibtfalse()
bei folgendem Element:<elt nonempty="…" />
. false()
-
Gibt „Falsch“ zurück.
floor()
-
Gibt den nächst niedrigeren Wert als Ganzzahl zurück.
last()
-
Gibt die Anzahl der Datensätze der gleichnamigen Geschwister-Elemente zurück.
local-name()
-
Liefert den Namen des aktuellen Knotens zurück (ohne Namensraum).
lower-case(<text>)
-
Gibt den Text als Kleinbuchstaben zurück.
lower-case('Text')
ergibttext
. matches(<Text>,<Regexp>[,<flags>])
-
Prüft, ob der Text auf den Regulären Ausdruck Regexp passt. Flags kann ein oder mehrere Zeichen von
sim
sein (siehe https://www.w3.org/TR/xpath-functions-31/#flags). Beispiel:matches("banana", "^(.a)+$")
ergibt „Wahr“. max(<Zahl>[, <Zahl>, …])
-
Liefert das Maximum der Werte zurück:
max(1.1,2.2,3.3,4.4)
ergibt4.4
. min(<Zahl>[, <Zahl>, …])
-
Liefert das Minimum der Werte zurück:
min(1.1,2.2,3.3,4.4)
ergibt1.1
. namespace-uri([nodeset])
-
Gibt den Namensraum des ersten Knotens der Sequenz zurück. Voreinstellung der Sequenz ist der aktuelle Kontext.
normalize-space(<text>)
-
Gibt den Text ohne führende und nachstehende Leerzeichen zurück. Alle Zeilenvorschübe werden durch Leerzeichen ersetzt. Mehrfach hintereinander auftretende Leerzeichen/Zeilenvorschübe werden durch ein einzelnes Leerzeichen ersetzt.
not()
-
Negiert den Wahrheitswert des Arguments. Beispiel:
not(true())
ergibtfalse()
. number(<Wert>)
-
Konvertiert den Wert in eine Zahl (double). Falls die Zahl nicht konvertiert werden kann, ist die Rückgabe “not a number” (NaN).
position()
-
Ermittelt die Position des aktuellen Datensatzes. Anwendungsfall:
<Switch><Case test="position() = last()"> …
führt den Inhalt des<Case>
-Abschnittes nur beim letzten Element aus. replace(<Eingabe>,<Regexp>, <Ersetzung>)
-
Ersetzt die Eingabe mit dem regulären Ausdruck durch den Ersetzungstext. Beispiel:
replace('banana', 'a', 'o')
ergibtbonono
. Beispiel mit Ersetzungen:replace('W151TBH','^[A-Z]([0-9]+)[A-Z]+$', '$1')
ergibt 151. round(<Zahl>,<Zahl>
)`-
Rundet die angegebene Zahl im ersten Argument auf die Anzahl der Nachkommastellen im zweiten Argument.
root(<element>)
-
Gibt das Wurzelelement des Elements zurück.
ends-with( <string>, <string>)
-
Gibt wahr (true) zurück, wenn die erste Zeichenkette mit der zweiten endet. Beispiel:
ends-with ( "tattoo", "too")
ergibttrue
. starts-with( <string>, <string>)
-
Gibt wahr (true) zurück, wenn die erste Zeichenkette mit der zweiten anfängt. Beispiel:
ends-with ( "tattoo", "tat")
ergibttrue
. string(<Sequenz>)
-
Gibt den Textwert der Sequenz zurück, d. h. den Inhalt der Elemente.
string-join(<Sequenz>, Separator)
-
Gibt den Textwert der Sequenz zurück, wobei alle Elemente durch den Separator getrennt werden.
string-length(<string>)
-
Gibt die Länge der Zeichenkette zurück. Multibyte UTF-8 Sequenzen werden als eine Position gezählt.
string-to-codepoints( <string> )
-
Konvertiere die Zeichenkette in eine Sequenz von Codepoints.
substring(<input>,<start>[,<length>])
-
Gibt einen Teil der Zeichenkette aus
input
zurück, die beistart
anfängt und (optional) die Längelength
hat.substring('Goldfarb', 5, 3)
gibtfar
zurück.start
kann auch (entgegen der XPath-Spezifikation) auch negativ sein, dann wird vom Ende der Eingabe gezählt. substring-after(<string>,<string>])
-
Gibt den Inhalt der ersten Zeichenkette zurück, der ab der zweiten Zeichenkette vorkommt: Beispiel:
substring-after ( "tattoo", "tat")
ergibt"too"
. substring-before(<string>,<string>])
-
Gibt den Inhalt der ersten Zeichenkette zurück, der bis zur zweiten Zeichenkette geht: Beispiel:
substring-before ( "tattoo", "attoo")
ergibt"t"
. tokenize(<Eingabe>,<Regexp>)
-
Die Rückgabe ist eine Sequenz von Zeichenketten. Die Eingabe wird von links nach rechts gelesen. Sobald eine Stelle gefunden wird, auf die der Reguläre Ausdruck passt, wird die bisherige Eingabe zurück gegeben. Beispiel (aus M. Kays XPath / XSLT-Buch):
tokenize("Go home, Jack!", "\W+")
ergibt die Sequenz"Go", "home", "Jack", ""
. true()
-
Gibt „Wahr“ zurück.
unparsed-text(<dateiname>)
-
Gibt den Inhalt der Datei zurück ohne dass sie interpretiert wird.
upper-case()
-
Wandelt den Text in Großbuchstaben:
upper-case('Text')
ergibtTEXT
.